From 5e6609161149c93259c11f60ac75e184b9e252fe Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 13:51:28 -0500 Subject: [PATCH 01/10] can hit reservoir for order --- lib/reservoir-helper.ts | 34 ++++ lib/types.ts | 1 + package.json | 4 +- tasks/callExecute.ts | 1 + tasks/callExecuteWithOffchain.ts | 1 + tasks/executeReservoirOrder.ts | 268 +++++++++++++++++++++++++++++++ tasks/index.ts | 4 +- tasks/submitDiscrete.ts | 1 + tasks/submitOffchainOffer.ts | 1 + yarn.lock | 8 +- 10 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 lib/reservoir-helper.ts create mode 100644 tasks/executeReservoirOrder.ts diff --git a/lib/reservoir-helper.ts b/lib/reservoir-helper.ts new file mode 100644 index 0000000..24d92a2 --- /dev/null +++ b/lib/reservoir-helper.ts @@ -0,0 +1,34 @@ + +import axios from 'axios' + +require('dotenv').config() + +const RESERVOIR_API_KEY = process.env.RESERVOIR_API_KEY! + +export async function fetchReservoirOrderById( + {orderId}:{orderId:string} + ) : Promise { + + + const apiUrl = new URL('https://api.reservoir.tools/orders/asks/v4') + + apiUrl.searchParams.set('ids', `${orderId}`) + apiUrl.searchParams.set('includeRawData', 'true') + + const headers = { + 'x-api-key': RESERVOIR_API_KEY, + 'accept':'*/*', + + } + + console.log(apiUrl.toString()) + const response = await axios.get(apiUrl.toString(), { headers }) + + + const orders = response.data.orders + + if(!orders || orders.length == 0) return undefined + + return orders + +} \ No newline at end of file diff --git a/lib/types.ts b/lib/types.ts index e23c081..2d03fa6 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -20,6 +20,7 @@ export interface SubmitBidArgs { interestRate: string referralAddress: string metadataURI: string + marketId: string } diff --git a/package.json b/package.json index df82d69..4f5486f 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,11 @@ "author": "", "license": "MIT", "dependencies": { + "@clarity-credit/anpl-sdk": "^0.1.3", "@types/node-fetch": "^2.6.1", "alchemy-sdk": "^2.0.5-rc1", - "@clarity-credit/anpl-sdk": "^0.1.3", "argv": "^0.0.2", - "axios": "^1.2.0", + "axios": "^1.2.3", "dotenv": "^16.0.1", "moment": "^2.29.3", "opensea-js": "^3.0.4", diff --git a/tasks/callExecute.ts b/tasks/callExecute.ts index 05dcede..bd4b69f 100644 --- a/tasks/callExecute.ts +++ b/tasks/callExecute.ts @@ -138,6 +138,7 @@ export async function callExecute(): Promise { interestRate:submitBidArgs.interestRate, referralAddress: submitBidArgs.referralAddress, metadataURI: submitBidArgs.metadataURI , + marketId: "2" } diff --git a/tasks/callExecuteWithOffchain.ts b/tasks/callExecuteWithOffchain.ts index 0f744a2..d46ca17 100644 --- a/tasks/callExecuteWithOffchain.ts +++ b/tasks/callExecuteWithOffchain.ts @@ -183,6 +183,7 @@ export async function callExecuteWithOffchain(): Promise { interestRate:submitBidArgs.interestRate, referralAddress: submitBidArgs.referralAddress, metadataURI: submitBidArgs.metadataURI , + marketId: executeConfig.marketplaceId.toString() } diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts new file mode 100644 index 0000000..4362fa6 --- /dev/null +++ b/tasks/executeReservoirOrder.ts @@ -0,0 +1,268 @@ + + + + + + +import axios from 'axios' +import { toChecksumAddress } from 'ethereumjs-util' +import {Contract, Wallet, providers, utils, BigNumber, ethers} from 'ethers' + +import { getRpcUrlFromNetworkName, networkNameFromChainId } from '../lib/app-helper' + +import { buildExecuteParams, calculateTotalPrice, generateBNPLOrderSignature, performCraRequest, readSignatureVersionFromBNPLMarketContract } from '../lib/bnpl-helper' +import { fetchReservoirOrderById } from '../lib/reservoir-helper' + +import { BasicOrderParams, SubmitBidArgs } from '../lib/types' + +require('dotenv').config() + +const borrowerPrivateKey = process.env.BORROWER_PRIVATE_KEY + +const lenderPrivateKey = process.env.LENDER_PRIVATE_KEY + + + + + + + +/* +const executeConfig = { + + marketplaceId: 2 + +} + + +const craResponseSample = require('../test/data/sampleCraOutput.json') + + +let tokenInputData = require('../data/tokenInputData.json') +let networkName = networkNameFromChainId( tokenInputData.chainId ) +let contractsConfig = require('../data/contractsConfig.json')[networkName] + +const ProxyAdminInterface = require('../abi/OpenZeppelinTransparentProxyAdmin.abi.json') + +const rpcURI = getRpcUrlFromNetworkName(networkName) + +//was 0x519b957ecaa80C5aEd4C5547Ff2Eac3ff5dE229c +const tellerV2Config = { + address: contractsConfig.tellerV2.address, + abi: require('../abi/TellerV2.json') +} + +const bnplConfig = { + address: contractsConfig.BNPLContract.address, + abi: require('../abi/BNPLMarketV3.json') + } +*/ + +/* + +Test w tenderly test RPC + +*/ + + + +export async function executeReservoirOrder(): Promise { + + const orderId = "0x36676cd9406a187400fc3154d3e1e214374e4c907e31eef963b0dcef366cb15b" + + const orderResponse = await fetchReservoirOrderById({orderId}) + + console.log({orderResponse}) + + + + /* + + let rpcProvider = new providers.JsonRpcProvider( rpcURI ) + + let tellerV2Instance = new Contract(tellerV2Config.address,tellerV2Config.abi, rpcProvider) + let bnplContractInstance = new Contract(bnplConfig.address,bnplConfig.abi,rpcProvider) + + + let signatureVersion = await readSignatureVersionFromBNPLMarketContract( bnplContractInstance ) + + let craInputs = { + asset_contract_address:tokenInputData.tokenAddress, + token_id:tokenInputData.tokenId, + quantity: tokenInputData.tokenQuantity, + + chain_id:tokenInputData.chainId, + signature_version: signatureVersion + } + + // let craResponse = await performCraRequest( craInputs ) + let craResponse = {success:true, data: craResponseSample , error:'none'} + + console.log('meep', craResponse) + + + if(!craResponse.success || !craResponse.data) throw new Error('cra error '.concat(craResponse.error.toString())) + + + let executeParams = craResponse.data + + if(typeof(executeParams.submitBidArgs.metadataURI) == 'undefined'){ + executeParams.submitBidArgs.metadataURI = "ipfs://" + } + + + if(!borrowerPrivateKey) throw new Error('Missing borrowerPrivateKey') + + + if(!lenderPrivateKey) throw new Error('Missing lenderPrivateKey') + + let borrowerWallet = new Wallet(borrowerPrivateKey).connect(rpcProvider) + + let lenderWallet = new Wallet(lenderPrivateKey).connect(rpcProvider) + + console.log(`calling execute using account ${borrowerWallet.address}`) + + + const submitBidArgs:SubmitBidArgs = executeParams.submitBidArgs + + + submitBidArgs.borrower = borrowerWallet.address + submitBidArgs.lender = borrowerWallet.address + + let value:BigNumber = BigNumber.from(submitBidArgs.downPayment) + + let lenderAddress = lenderWallet.address + + let basicOrderParams:BasicOrderParams = executeParams.basicOrderParams + + if(!basicOrderParams.offererConduitKey){ + throw new Error('Missing offererConduitKey') + } + + */ + + + /* + const chainId = tokenInputData.chainId + + + + // find implementation address via proxy admin + + let bnplContractProxyAddress = contractsConfig.BNPLContract.address + + const proxyAdminContract = new Contract( contractsConfig.proxyAdmin.address, ProxyAdminInterface, rpcProvider ) + let implementationContractAddress = await proxyAdminContract.getProxyImplementation( bnplContractProxyAddress ) + + implementationContractAddress = toChecksumAddress(implementationContractAddress) + + if(typeof(implementationContractAddress) == 'undefined'){ + return {success:false, error:"Could not get implementation address"} + } + + + + + + let lenderSignature = await generateBNPLOrderSignature( + submitBidArgs, + basicOrderParams, + lenderWallet, + chainId, + implementationContractAddress + ) + + let borrowerSignature = await generateBNPLOrderSignature( + submitBidArgs, + basicOrderParams, + borrowerWallet, + chainId, + implementationContractAddress + ) + + + + + let lenderHasApproved = await tellerV2Instance.hasApprovedMarketForwarder(executeConfig.marketplaceId, bnplContractInstance.address, lenderAddress) + console.log('lender has approved BNPL as forwarder: ',lenderHasApproved, lenderAddress) + + if(!lenderHasApproved) { + console.error(`ERROR: lender ${lenderAddress} has not approved bnpl as forwarder `) + return + } + + + let borrowerHasApproved = await tellerV2Instance.hasApprovedMarketForwarder(executeConfig.marketplaceId, bnplContractInstance.address, lenderAddress) + console.log('lender has approved BNPL as forwarder: ',borrowerHasApproved, lenderAddress) + + if(!borrowerHasApproved) { + console.error(`ERROR: lender ${lenderAddress} has not approved bnpl as forwarder `) + return + } + + + + + + //fix it for now to remove referral and sig expir + let formattedSubmitBidArgs:SubmitBidArgs = { + lender: lenderAddress, + totalPurchasePrice: submitBidArgs.totalPurchasePrice, + principal: submitBidArgs.principal, + downPayment: submitBidArgs.downPayment, + duration: submitBidArgs.duration, + signatureExpiration: submitBidArgs.signatureExpiration, + interestRate:submitBidArgs.interestRate, + referralAddress: submitBidArgs.referralAddress, + metadataURI: submitBidArgs.metadataURI , + } + + + console.log('passing in params', + formattedSubmitBidArgs, + basicOrderParams , + borrowerSignature, + lenderSignature + ) + + // basicOrderParams.offererConduitKey = "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000" + //basicOrderParams.signature= "0x5c8a6fd29db37f9b53fcf1cce21c62e68ca0684c28e0320cb27e8568210baf37644e1258430b698cfe533334621ddd82a12e46dd184823ab318b8ee6ff0fa2dd1c" + + //this address needs to approve the forwarder on tellerv2 + // lenderAddress = "0xF4dAb24C52b51cB69Ab62cDE672D3c9Df0B39681" + + //Set price to 1 Gwei + let gasPrice = utils.hexlify(8_000_000_000); + //Set max gas limit to 4M + var gasLimit = utils.hexlify(10_000_000); + + + if((basicOrderParams.basicOrderType) > 22){ + throw new Error('invalid basic order type') + } + + + */ + + /* let unsignedTx = await bnplContractInstance + .populateTransaction + .executeWithOffchainSignatures( + formattedSubmitBidArgs, + basicOrderParams, + borrowerSignature, + lenderSignature + + , {value, gasLimit, gasPrice} ) + + + + let response = await borrowerWallet.sendTransaction(unsignedTx); + console.log('response',response)*/ + + + return true + } + + + + diff --git a/tasks/index.ts b/tasks/index.ts index b939cce..0a271ba 100644 --- a/tasks/index.ts +++ b/tasks/index.ts @@ -8,6 +8,7 @@ import { approveMarket } from './approveMarket' import {getNFTsOwned} from './getNFTsOwned' import {callExecuteWithOffchain} from './callExecuteWithOffchain' import {submitOffchainOffer} from './submitOffchainOffer' +import {executeReservoirOrder} from './executeReservoirOrder' const yargs = require('yargs').argv @@ -19,7 +20,8 @@ const taskMap: any = { fetchCraResponse, approveMarket, getNFTsOwned, - submitOffchainOffer + submitOffchainOffer, + executeReservoirOrder // matchOrder } diff --git a/tasks/submitDiscrete.ts b/tasks/submitDiscrete.ts index a3397c2..888df08 100644 --- a/tasks/submitDiscrete.ts +++ b/tasks/submitDiscrete.ts @@ -120,6 +120,7 @@ export async function submitDiscrete(): Promise { interestRate:submitBidArgs.interestRate, referralAddress: submitBidArgs.referralAddress, metadataURI: submitBidArgs.metadataURI , + marketId: "2" } diff --git a/tasks/submitOffchainOffer.ts b/tasks/submitOffchainOffer.ts index 2eaa5d8..8521eaf 100644 --- a/tasks/submitOffchainOffer.ts +++ b/tasks/submitOffchainOffer.ts @@ -150,6 +150,7 @@ export async function submitOffchainOffer(): Promise { interestRate:submitBidArgs.interestRate, referralAddress: submitBidArgs.referralAddress, metadataURI: submitBidArgs.metadataURI , + marketId:"2" } let borrowerSignature = await signOffchainOffer({ diff --git a/yarn.lock b/yarn.lock index df6b388..dce8325 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1480,10 +1480,10 @@ axios@^0.26.1: dependencies: follow-redirects "^1.14.8" -axios@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383" - integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw== +axios@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.3.tgz#31a3d824c0ebf754a004b585e5f04a5f87e6c4ff" + integrity sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" From 03b17e0b213ade3019870a08892f818d25328c97 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 14:12:42 -0500 Subject: [PATCH 02/10] getting closer --- data/contractsConfig.json | 1 + lib/reservoir-helper.ts | 193 ++++++++++++++++++++++++++++++++- lib/types.ts | 80 +++++++++++++- tasks/executeReservoirOrder.ts | 145 ++++++++++++++++++++++--- 4 files changed, 400 insertions(+), 19 deletions(-) diff --git a/data/contractsConfig.json b/data/contractsConfig.json index 29514fa..c292622 100644 --- a/data/contractsConfig.json +++ b/data/contractsConfig.json @@ -40,6 +40,7 @@ "BNPLContract":{ "address": "0x260C32eB38D1403bd51B83B5b7047812C70B7845" }, + "seaport":{ "address": "0x00000000006c3852cbef3e08e8df289169ede581" }, diff --git a/lib/reservoir-helper.ts b/lib/reservoir-helper.ts index 24d92a2..a608113 100644 --- a/lib/reservoir-helper.ts +++ b/lib/reservoir-helper.ts @@ -1,10 +1,16 @@ import axios from 'axios' +import { BigNumber } from 'ethers' +import { AdditionalRecipient, BasicOrderParams, BasicOrderParamsResponse, ReservoirOrder, SeaportProtocolData, SeaportProtocolParameters } from './types' require('dotenv').config() const RESERVOIR_API_KEY = process.env.RESERVOIR_API_KEY! +const BLANK_SIGNATURE = + '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + + export async function fetchReservoirOrderById( {orderId}:{orderId:string} ) : Promise { @@ -29,6 +35,191 @@ export async function fetchReservoirOrderById( if(!orders || orders.length == 0) return undefined - return orders + return orders[0] + +} + + + +export function formatReservoirOrder(order: ReservoirOrder): { + order: ReservoirOrder + basicOrderParams?: BasicOrderParamsResponse + } { + + + if(!order.rawData.consideration){ + console.error("No consideration within raw data") + return {order} + } + + const protocolData: SeaportProtocolData = { + parameters: { + consideration: order.rawData.consideration, + offerer: order.rawData.offerer, + zone: order.rawData.zone, + offer: order.rawData.offer, + startTime: order.rawData.startTime, + endTime: order.rawData.endTime, + orderType: order.rawData.orderType, + zoneHash: order.rawData.zoneHash, + salt: order.rawData.salt, + totalOriginalConsiderationItems: + order.rawData.consideration.length.toString(), + conduitKey: order.rawData.conduitKey, + + parameterOrderType: order.rawData.orderType, //is this correct ? probably. + }, + signature: order.rawData.signature, + } + + const generatedOrderParams: BasicOrderParams = + generateBasicOrderParamsFromSeaport(protocolData) + + return { + order: order, + basicOrderParams: formatBasicOrderParams(generatedOrderParams), + } + } + + +export function generateBasicOrderParamsFromSeaport( + orderData: SeaportProtocolData + ): BasicOrderParams { + const orderParameters: SeaportProtocolParameters = orderData.parameters + + const basicOrderParams: BasicOrderParams = { + considerationToken: orderParameters.consideration[0].token, //payment token + considerationIdentifier: BigNumber.from( + orderParameters.consideration[0].identifierOrCriteria + ), // not sure what significance this has + considerationAmount: BigNumber.from( + orderParameters.consideration[0].endAmount + ), // using the first element in the consideration array as per Andy's suggestion + offerer: orderParameters.offerer, + zone: orderParameters.zone, + offerToken: orderParameters.offer[0].token, //nft_contract + offerIdentifier: BigNumber.from( + orderParameters.offer[0].identifierOrCriteria + ), //token id + offerAmount: orderParameters.offer[0].endAmount, //quantity + basicOrderType: getBasicOrderType( + orderParameters.orderType, + orderParameters.consideration[0].itemType, + orderParameters.offer[0].itemType + ), + + startTime: BigNumber.from(orderParameters.startTime), + endTime: BigNumber.from(orderParameters.endTime), + zoneHash: orderParameters.zoneHash, + salt: BigNumber.from(orderParameters.salt).toHexString(), + offererConduitKey: orderParameters.conduitKey, + fulfillerConduitKey: + '0x0000000000000000000000000000000000000000000000000000000000000000', + totalOriginalAdditionalRecipients: BigNumber.from( + orderParameters.totalOriginalConsiderationItems + ).sub(1), + additionalRecipients: getAdditionalRecipients(orderParameters), + signature: orderData.signature ? orderData.signature : BLANK_SIGNATURE, + } + return basicOrderParams + } + + + +function getAdditionalRecipients(parameters: SeaportProtocolParameters): any[] { + const recipientsArray: object[] = [] + + for (let i = 1; i < parameters.consideration.length; i++) { + const additionalRecipient = { + amount: BigNumber.from(parameters.consideration[i].endAmount), + recipient: parameters.consideration[i].recipient, + } + + recipientsArray.push(additionalRecipient) + } + return recipientsArray + } + + +export function formatBasicOrderParams( + basicOrderParams: any + ): BasicOrderParamsResponse { + return { + considerationToken: basicOrderParams.considerationToken, + considerationIdentifier: BigNumber.from( + basicOrderParams.considerationIdentifier + ).toString(), + considerationAmount: BigNumber.from( + basicOrderParams.considerationAmount + ).toString(), + offerer: basicOrderParams.offerer, + zone: basicOrderParams.zone, + offerToken: basicOrderParams.offerToken, + offerIdentifier: BigNumber.from( + basicOrderParams.offerIdentifier + ).toString(), + offerAmount: BigNumber.from(basicOrderParams.offerAmount).toString(), + basicOrderType: basicOrderParams.basicOrderType, + startTime: BigNumber.from(basicOrderParams.startTime).toString(), + endTime: BigNumber.from(basicOrderParams.endTime).toString(), + zoneHash: basicOrderParams.zoneHash, + salt: basicOrderParams.salt, + offererConduitKey: basicOrderParams.offererConduitKey, + fulfillerConduitKey: basicOrderParams.fulfillerConduitKey, + totalOriginalAdditionalRecipients: BigNumber.from( + basicOrderParams.totalOriginalAdditionalRecipients + ).toString(), + additionalRecipients: basicOrderParams.additionalRecipients.map( + (r: AdditionalRecipient) => { + return { + amount: BigNumber.from(r.amount).toString(), + recipient: r.recipient, + } + } + ), + signature: basicOrderParams.signature, + } + } + + +function getBasicOrderType( + orderType: number, + considerationItemType: number, + offerItemType: number + ): number { + let basicOrderRouteType = 10 + //refer to the above enums to understand if else statements + if (considerationItemType == 0 && offerItemType == 2) { + basicOrderRouteType = 0 + } else if ( + considerationItemType == 0 && + (offerItemType == 3 || offerItemType == 5) + ) { + basicOrderRouteType = 1 + } else if ( + considerationItemType == 1 && + (offerItemType == 2 || offerItemType == 4) + ) { + basicOrderRouteType = 2 + } else if ( + considerationItemType == 1 && + (offerItemType == 3 || offerItemType == 5) + ) { + basicOrderRouteType = 3 + } else if ( + (considerationItemType == 2 || considerationItemType == 4) && + offerItemType == 1 + ) { + basicOrderRouteType = 4 + } else if ( + (considerationItemType == 3 || considerationItemType == 5) && + offerItemType == 1 + ) { + basicOrderRouteType = 5 + } + + +const basicOrderType = orderType + 4 * basicOrderRouteType +return basicOrderType } \ No newline at end of file diff --git a/lib/types.ts b/lib/types.ts index 2d03fa6..94a0532 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,6 +1,84 @@ import { BigNumber } from "ethers"; + + +export interface ReservoirOrderRawData { + kind: string + salt: string + zone: string + offer: Consideration[] + counter: string + endTime: string + offerer: string + zoneHash: string + orderType: number + signature: string + startTime: string + conduitKey: string + consideration: Consideration[] +} +export interface ReservoirOrder { + id: string + kind: string + status: string + contract: string + rawData: ReservoirOrderRawData +} + + + +export interface BasicOrderParamsResponse { + considerationToken: string + considerationIdentifier: string //bn + considerationAmount: string //bn + offerer: string + zone: string + offerToken: string + offerIdentifier: string //bn + offerAmount: string + basicOrderType: number + startTime: string //bn + endTime: string //bn + zoneHash: string + salt: string + offererConduitKey: string + fulfillerConduitKey: string + totalOriginalAdditionalRecipients: string //bn + additionalRecipients: AdditionalRecipient[] + signature: string +} +export interface AdditionalRecipient { + amount: string //bn + recipient: string +} + +export interface SeaportProtocolData { + // was opensearesponse + // nftPrice: string; + parameters: SeaportProtocolParameters + signature: string +} + + +export interface SeaportProtocolParameters { + // was opensearesponseparameters + // nftPrice: string; + consideration: Consideration[] + parameterOrderType: number + offerer: string + zone: string + offer: Consideration[] + startTime: string + endTime: string + orderType: number + zoneHash: string + salt: string + totalOriginalConsiderationItems?: string + conduitKey: string +} + + export interface DomainData { name: string version: string @@ -75,7 +153,7 @@ export interface Consideration { startAmount: string, endAmount: string, itemType: number - + recipient?: string } /* diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index 4362fa6..4ca4db4 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -11,9 +11,9 @@ import {Contract, Wallet, providers, utils, BigNumber, ethers} from 'ethers' import { getRpcUrlFromNetworkName, networkNameFromChainId } from '../lib/app-helper' import { buildExecuteParams, calculateTotalPrice, generateBNPLOrderSignature, performCraRequest, readSignatureVersionFromBNPLMarketContract } from '../lib/bnpl-helper' -import { fetchReservoirOrderById } from '../lib/reservoir-helper' +import { fetchReservoirOrderById, formatReservoirOrder } from '../lib/reservoir-helper' -import { BasicOrderParams, SubmitBidArgs } from '../lib/types' +import { BasicOrderParams, ReservoirOrder, ReservoirOrderRawData, SubmitBidArgs } from '../lib/types' require('dotenv').config() @@ -25,7 +25,23 @@ const lenderPrivateKey = process.env.LENDER_PRIVATE_KEY +const networkName = 'mainnet' + +let contractsConfig = require('../data/contractsConfig.json')[networkName] + +const rpcURI = getRpcUrlFromNetworkName(networkName) +const tellerV2Config = { + address: contractsConfig.tellerV2.address, + abi: require('../abi/TellerV2.json') +} +const bnplConfig = { + address: contractsConfig.BNPLContract.address, + abi: require('../abi/BNPLMarketV3.json') +} + +const chainId = "1" +const marketId = "6" /* const executeConfig = { @@ -43,24 +59,12 @@ let networkName = networkNameFromChainId( tokenInputData.chainId ) let contractsConfig = require('../data/contractsConfig.json')[networkName] const ProxyAdminInterface = require('../abi/OpenZeppelinTransparentProxyAdmin.abi.json') - -const rpcURI = getRpcUrlFromNetworkName(networkName) - -//was 0x519b957ecaa80C5aEd4C5547Ff2Eac3ff5dE229c -const tellerV2Config = { - address: contractsConfig.tellerV2.address, - abi: require('../abi/TellerV2.json') -} - -const bnplConfig = { - address: contractsConfig.BNPLContract.address, - abi: require('../abi/BNPLMarketV3.json') - } + */ /* -Test w tenderly test RPC +Test w tenderly test RPC ? */ @@ -70,10 +74,117 @@ export async function executeReservoirOrder(): Promise { const orderId = "0x36676cd9406a187400fc3154d3e1e214374e4c907e31eef963b0dcef366cb15b" - const orderResponse = await fetchReservoirOrderById({orderId}) + const orderResponse:ReservoirOrder|undefined = await fetchReservoirOrderById({orderId}) console.log({orderResponse}) + if(!orderResponse){ + throw new Error('No matching order from reservoir') + } + + const {basicOrderParams} = formatReservoirOrder( orderResponse ) + + console.log({basicOrderParams}) + + + if(!basicOrderParams){ + throw new Error('Unable to build basic order params') + } + + const basicOrderParamsFormatted:BasicOrderParams = { + considerationToken: basicOrderParams.considerationToken, + considerationIdentifier: BigNumber.from(basicOrderParams.considerationIdentifier), + considerationAmount: BigNumber.from(basicOrderParams.considerationAmount), + offerer: basicOrderParams.offerer, + zone: basicOrderParams.zone, + offerToken: basicOrderParams.offerToken, + offerIdentifier: BigNumber.from(basicOrderParams.offerIdentifier), + offerAmount: basicOrderParams.offerAmount, + basicOrderType: basicOrderParams.basicOrderType, + startTime: BigNumber.from(basicOrderParams.startTime), + endTime: BigNumber.from(basicOrderParams.endTime), + zoneHash: basicOrderParams.zoneHash, + salt: basicOrderParams.salt, + offererConduitKey: basicOrderParams.offererConduitKey, + fulfillerConduitKey: basicOrderParams.fulfillerConduitKey, + totalOriginalAdditionalRecipients: BigNumber.from(basicOrderParams.totalOriginalAdditionalRecipients), + additionalRecipients: basicOrderParams.additionalRecipients, + signature: basicOrderParams.signature + } + + + + + + + let rpcProvider = new providers.JsonRpcProvider( rpcURI ) + + let tellerV2Instance = new Contract(tellerV2Config.address,tellerV2Config.abi, rpcProvider) + let bnplContractInstance = new Contract(bnplConfig.address,bnplConfig.abi,rpcProvider) + + + + let submitBidArgs:SubmitBidArgs = { + totalPurchasePrice: '', + principal: '', + downPayment: '', + duration: '', + signatureExpiration: '', + interestRate: '', + referralAddress: '', + metadataURI: '', + marketId: '' + } + + const implementationContractAddress = "0x3bf7f0d0fa47f2101f67bd530f1be7ad05d90321" + + + let borrowerSignature = await generateBNPLOrderSignature( + submitBidArgs, + basicOrderParamsFormatted, + borrowerWallet, + parseInt(chainId), + implementationContractAddress + ) + + + //fix it for now to remove referral and sig expir + let formattedSubmitBidArgs:SubmitBidArgs = { + lender: lenderAddress, + totalPurchasePrice: submitBidArgs.totalPurchasePrice, + principal: submitBidArgs.principal, + downPayment: submitBidArgs.downPayment, + duration: submitBidArgs.duration, + signatureExpiration: submitBidArgs.signatureExpiration, + interestRate:submitBidArgs.interestRate, + referralAddress: submitBidArgs.referralAddress, + metadataURI: submitBidArgs.metadataURI , + marketId + } + + + + //Set price to 1 Gwei + let gasPrice = utils.hexlify(8_000_000_000); + //Set max gas limit to 4M + var gasLimit = utils.hexlify(10_000_000); + + + + let unsignedTx = await bnplContractInstance + .populateTransaction + .executeWithOffchainSignatures( + formattedSubmitBidArgs, + basicOrderParams, + borrowerSignature, + lenderSignature + , {value, gasLimit, gasPrice} ) + + + + let response = await borrowerWallet.sendTransaction(unsignedTx); + console.log('response',response) + /* From e1c8e3a9b509ac414455ee76449520f62ff2d720 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 14:55:55 -0500 Subject: [PATCH 03/10] closer --- lib/bnpl-helper.ts | 33 ++++++++--- lib/teller-v2-lending-helper.ts | 45 +++++++++++++++ lib/types.ts | 12 +++- package.json | 2 +- tasks/callExecuteWithOffchain.ts | 19 +++--- tasks/executeReservoirOrder.ts | 99 +++++++++++++++++++++----------- yarn.lock | 8 +-- 7 files changed, 163 insertions(+), 55 deletions(-) create mode 100644 lib/teller-v2-lending-helper.ts diff --git a/lib/bnpl-helper.ts b/lib/bnpl-helper.ts index eb14209..cb5faaf 100644 --- a/lib/bnpl-helper.ts +++ b/lib/bnpl-helper.ts @@ -2,15 +2,15 @@ import { parseFeeMethod, parseHowToCall, parseMetadata, parseSaleKind, WyvernAto import {BigNumber, Contract, ethers,Signer,Wallet} from 'ethers' - -import moment from 'moment' - + + import { NULL_BLOCK_HASH } from 'opensea-js/lib/constants' import { OpenseaHelper, SignedOrder, UnhashedOrder } from '../lib/opensea-helper' -import { SubmitBidArgs, ContractsConfig, CraResponse, ExecuteParams, BasicOrderParams } from "./types" +import { SubmitBidArgs, ContractsConfig, CraResponse, ExecuteParams, BasicOrderParams, DomainData } from "./types" import { axiosPostRequest } from "./axios-helper" import { craSign } from "./cra-signer" +import { ISignOfferSignerConfig, signOffchainOffer } from "@clarity-credit/anpl-sdk" require('dotenv').config() @@ -71,15 +71,32 @@ export function buildExecuteParams(inputData:CraResponse ): ExecuteParams { } + + export async function generateBNPLOrderSignature( submitBidArgs:SubmitBidArgs, basicOrderParams:BasicOrderParams, + domainData: DomainData, wallet: Wallet, - chainId: number, - implementationContractAddress: string ){ - let signatureVersion = 3 + const signConfig :ISignOfferSignerConfig = { + submitBidArgs, + basicOrderParams, + domainData, + + //@ts-ignore + signer:wallet, + + } + + + + const signature = signOffchainOffer(signConfig) + + + + /* let signatureVersion = 3 let signatureResponse = await craSign( submitBidArgs, @@ -95,5 +112,5 @@ export function buildExecuteParams(inputData:CraResponse ): ExecuteParams { return signatureResponse.data } - return undefined + return undefined */ } \ No newline at end of file diff --git a/lib/teller-v2-lending-helper.ts b/lib/teller-v2-lending-helper.ts new file mode 100644 index 0000000..d074e51 --- /dev/null +++ b/lib/teller-v2-lending-helper.ts @@ -0,0 +1,45 @@ +import { BigNumber } from 'ethers' + +const FACTOR = 10_000 + +/* +For ANPL, marketFeePct is 500 (5%) and protocolFeePct is 5 (0.05%) + +*/ + +export function calculatePrincipalRequiredForBorrowerPayout( + expectedBorrowerPayment: BigNumber, //5000 ///amount of wei THE BORROWER must get out of this loan + marketFeePct: BigNumber, //500 + protocolFeePct: BigNumber //5 +): BigNumber { + const totalFeesPct = marketFeePct.add(protocolFeePct) + + const principalRequired = expectedBorrowerPayment + .mul(BigNumber.from(FACTOR)) + .div(BigNumber.from(FACTOR).sub(totalFeesPct)) //rounding UP + + // ---- If we are off by 1, subtract 1 + const calculatedBorrowerPayment = calculateBorrowerPayoutFromLoanPrincipal( + principalRequired, + marketFeePct, + protocolFeePct + ) + const offByError = calculatedBorrowerPayment.sub(expectedBorrowerPayment) + + return principalRequired.sub(offByError) +} + +//this is how it happens in solidity +export function calculateBorrowerPayoutFromLoanPrincipal( + loanPrincipal: BigNumber, + marketFeePct: BigNumber, + protocolFeePct: BigNumber +): BigNumber { + const marketFee = loanPrincipal.mul(marketFeePct).div(FACTOR) + + const protocolFee = loanPrincipal.mul(protocolFeePct).div(FACTOR) + + const borrowerPayout = loanPrincipal.sub(marketFee).sub(protocolFee) + + return borrowerPayout +} diff --git a/lib/types.ts b/lib/types.ts index 94a0532..ca01e1e 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -45,10 +45,10 @@ export interface BasicOrderParamsResponse { offererConduitKey: string fulfillerConduitKey: string totalOriginalAdditionalRecipients: string //bn - additionalRecipients: AdditionalRecipient[] + additionalRecipients: AdditionalRecipientResponse[] signature: string } -export interface AdditionalRecipient { +export interface AdditionalRecipientResponse { amount: string //bn recipient: string } @@ -197,11 +197,17 @@ export interface TellerInputs { offererConduitKey: string, fulfillerConduitKey: string, totalOriginalAdditionalRecipients: BigNumber, - additionalRecipients:object[], + additionalRecipients: AdditionalRecipient[], signature: string } + export interface AdditionalRecipient { + amount: BigNumber //bn + recipient: string +} + + export interface ExecuteParams { submitBidArgs: SubmitBidArgs, diff --git a/package.json b/package.json index 4f5486f..69aa0c6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "author": "", "license": "MIT", "dependencies": { - "@clarity-credit/anpl-sdk": "^0.1.3", + "@clarity-credit/anpl-sdk": "^0.3.5", "@types/node-fetch": "^2.6.1", "alchemy-sdk": "^2.0.5-rc1", "argv": "^0.0.2", diff --git a/tasks/callExecuteWithOffchain.ts b/tasks/callExecuteWithOffchain.ts index d46ca17..2d32c68 100644 --- a/tasks/callExecuteWithOffchain.ts +++ b/tasks/callExecuteWithOffchain.ts @@ -6,7 +6,7 @@ import { getRpcUrlFromNetworkName, networkNameFromChainId } from '../lib/app-hel import { buildExecuteParams, calculateTotalPrice, generateBNPLOrderSignature, performCraRequest, readSignatureVersionFromBNPLMarketContract } from '../lib/bnpl-helper' -import { BasicOrderParams, SubmitBidArgs } from '../lib/types' +import { BasicOrderParams, DomainData, SubmitBidArgs } from '../lib/types' require('dotenv').config() @@ -130,22 +130,27 @@ export async function callExecuteWithOffchain(): Promise { - + const domainData:DomainData= { + name: '', + version: '', + chainId: 0, + verifyingContract: '' + } let lenderSignature = await generateBNPLOrderSignature( submitBidArgs, - basicOrderParams, + basicOrderParams, + domainData , lenderWallet, - chainId, - implementationContractAddress + ) let borrowerSignature = await generateBNPLOrderSignature( submitBidArgs, basicOrderParams, + domainData , borrowerWallet, - chainId, - implementationContractAddress + ) diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index 4ca4db4..bb2882a 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -12,17 +12,16 @@ import { getRpcUrlFromNetworkName, networkNameFromChainId } from '../lib/app-hel import { buildExecuteParams, calculateTotalPrice, generateBNPLOrderSignature, performCraRequest, readSignatureVersionFromBNPLMarketContract } from '../lib/bnpl-helper' import { fetchReservoirOrderById, formatReservoirOrder } from '../lib/reservoir-helper' +import { calculatePrincipalRequiredForBorrowerPayout } from '../lib/teller-v2-lending-helper' -import { BasicOrderParams, ReservoirOrder, ReservoirOrderRawData, SubmitBidArgs } from '../lib/types' +import { AdditionalRecipient, BasicOrderParams, DomainData, ReservoirOrder, ReservoirOrderRawData, SubmitBidArgs } from '../lib/types' require('dotenv').config() -const borrowerPrivateKey = process.env.BORROWER_PRIVATE_KEY - -const lenderPrivateKey = process.env.LENDER_PRIVATE_KEY - - +const borrowerPrivateKey = process.env.BORROWER_PRIVATE_KEY! +const lenderPrivateKey = process.env.LENDER_PRIVATE_KEY! + const networkName = 'mainnet' @@ -44,16 +43,15 @@ const chainId = "1" const marketId = "6" /* + const executeConfig = { marketplaceId: 2 } - const craResponseSample = require('../test/data/sampleCraOutput.json') - let tokenInputData = require('../data/tokenInputData.json') let networkName = networkNameFromChainId( tokenInputData.chainId ) let contractsConfig = require('../data/contractsConfig.json')[networkName] @@ -84,13 +82,18 @@ export async function executeReservoirOrder(): Promise { const {basicOrderParams} = formatReservoirOrder( orderResponse ) - console.log({basicOrderParams}) if(!basicOrderParams){ throw new Error('Unable to build basic order params') } + + const additionalRecipientsFormatted:AdditionalRecipient[] = basicOrderParams.additionalRecipients.map( (r:any) => {return { + amount: BigNumber.from(r.amount), + recipient: r.recipient + }}) + const basicOrderParamsFormatted:BasicOrderParams = { considerationToken: basicOrderParams.considerationToken, considerationIdentifier: BigNumber.from(basicOrderParams.considerationIdentifier), @@ -108,45 +111,74 @@ export async function executeReservoirOrder(): Promise { offererConduitKey: basicOrderParams.offererConduitKey, fulfillerConduitKey: basicOrderParams.fulfillerConduitKey, totalOriginalAdditionalRecipients: BigNumber.from(basicOrderParams.totalOriginalAdditionalRecipients), - additionalRecipients: basicOrderParams.additionalRecipients, + additionalRecipients: additionalRecipientsFormatted, signature: basicOrderParams.signature } + + let rpcProvider = new providers.JsonRpcProvider( rpcURI ) + + let tellerV2Instance = new Contract(tellerV2Config.address,tellerV2Config.abi, rpcProvider) + let bnplContractInstance = new Contract(bnplConfig.address,bnplConfig.abi,rpcProvider) + const borrowerWallet = new Wallet(borrowerPrivateKey) + const lenderWallet = new Wallet(lenderPrivateKey) - let rpcProvider = new providers.JsonRpcProvider( rpcURI ) - - let tellerV2Instance = new Contract(tellerV2Config.address,tellerV2Config.abi, rpcProvider) - let bnplContractInstance = new Contract(bnplConfig.address,bnplConfig.abi,rpcProvider) + const lenderAddress = lenderWallet.address + + + + const totalPurchasePrice = basicOrderParams.considerationAmount + const downPayment = BigNumber.from(totalPurchasePrice).div(2).toString() + const amountRequiredForLoan = BigNumber.from(totalPurchasePrice).sub( downPayment ) + + const principal = calculatePrincipalRequiredForBorrowerPayout( + amountRequiredForLoan, + BigNumber.from(0), //market fee for market 6 + BigNumber.from(5) //protocol fee + ).toString() let submitBidArgs:SubmitBidArgs = { - totalPurchasePrice: '', - principal: '', - downPayment: '', - duration: '', - signatureExpiration: '', - interestRate: '', - referralAddress: '', - metadataURI: '', - marketId: '' + totalPurchasePrice, + principal, + downPayment, + + duration: '28000', + signatureExpiration: (Date.now() + 90*60*1000).toString(), + interestRate: '300', + referralAddress: ethers.constants.AddressZero, + metadataURI: 'ipfs://', + marketId } const implementationContractAddress = "0x3bf7f0d0fa47f2101f67bd530f1be7ad05d90321" + const domainData:DomainData = { + name: 'Teller_BNPL_Market', + version: '3.5', + chainId: parseInt(chainId), + verifyingContract: implementationContractAddress + } let borrowerSignature = await generateBNPLOrderSignature( submitBidArgs, - basicOrderParamsFormatted, - borrowerWallet, - parseInt(chainId), - implementationContractAddress + basicOrderParamsFormatted, + domainData, + borrowerWallet, ) + let lenderSignature = await generateBNPLOrderSignature( + submitBidArgs, + basicOrderParamsFormatted, + domainData, + lenderWallet, + ) + //fix it for now to remove referral and sig expir let formattedSubmitBidArgs:SubmitBidArgs = { @@ -164,6 +196,10 @@ export async function executeReservoirOrder(): Promise { + console.log({basicOrderParams}) + console.log({formattedSubmitBidArgs}) + + //Set price to 1 Gwei let gasPrice = utils.hexlify(8_000_000_000); //Set max gas limit to 4M @@ -171,6 +207,8 @@ export async function executeReservoirOrder(): Promise { + let value:BigNumber = BigNumber.from(submitBidArgs.downPayment) + let unsignedTx = await bnplContractInstance .populateTransaction .executeWithOffchainSignatures( @@ -270,9 +308,7 @@ export async function executeReservoirOrder(): Promise { if(typeof(implementationContractAddress) == 'undefined'){ return {success:false, error:"Could not get implementation address"} } - - - + let lenderSignature = await generateBNPLOrderSignature( @@ -311,8 +347,7 @@ export async function executeReservoirOrder(): Promise { return } - - + //fix it for now to remove referral and sig expir diff --git a/yarn.lock b/yarn.lock index dce8325..e9d1eaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -113,10 +113,10 @@ ethers "~4.0.4" lodash "^4.17.11" -"@clarity-credit/anpl-sdk@^0.1.3": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@clarity-credit/anpl-sdk/-/anpl-sdk-0.1.8.tgz#ddc65305aac7bb6d351025796b27b84e3d5aea67" - integrity sha512-WIQEkN31RrU/QEmRgrGosjEyhdQ2TfTbk6erC8hfw58OTPvXjBla4snzs1h9NtBLu1uhH0IH4huRCRwGpYBkIw== +"@clarity-credit/anpl-sdk@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@clarity-credit/anpl-sdk/-/anpl-sdk-0.3.5.tgz#2dd7757da20ef96706c780d86372f0fac3859097" + integrity sha512-4f5ixYOOU6p//oDRhUf3/4JWGfDfQHbWDmIdc5xp2NQTjSJ5IGrRiVClVDvAZ5RomF8WJLVqbuAfAAVJZx837w== dependencies: ethers "^5.7.1" From c42309ec92e06ddb4038f712c5b6ef71f0d78f37 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 15:08:07 -0500 Subject: [PATCH 04/10] closer --- abi/BNPLMarketV3.json | 1320 +----------------------------- tasks/callExecuteWithOffchain.ts | 2 +- tasks/executeReservoirOrder.ts | 25 +- 3 files changed, 23 insertions(+), 1324 deletions(-) diff --git a/abi/BNPLMarketV3.json b/abi/BNPLMarketV3.json index 1e59a8a..a46b1aa 100644 --- a/abi/BNPLMarketV3.json +++ b/abi/BNPLMarketV3.json @@ -1,1319 +1 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "_tellerV2", - "type": "address" - }, - { - "internalType": "address", - "name": "_marketRegistry", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_marketId", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_wethAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "_trustedForwarder", - "type": "address" - }, - { - "internalType": "address", - "name": "_craSigner", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "discreteOrderId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "bidId", - "type": "uint256" - } - ], - "name": "AcceptedDiscreteOrder", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "bidId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "borrower", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "lender", - "type": "address" - } - ], - "name": "AssetPurchaseWithLoan", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "discreteOrderId", - "type": "uint256" - } - ], - "name": "CancelledDiscreteOrder", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "bidId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "tokenContract", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "ClaimedAsset", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "discreteOrderId", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "borrower", - "type": "address" - } - ], - "name": "CreatedDiscreteOrder", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "_feeRecipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "allowed", - "type": "bool" - } - ], - "name": "SetAllowedFeeRecipient", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint16", - "name": "pct", - "type": "uint16" - } - ], - "name": "SetReferralReward", - "type": "event" - }, - { - "inputs": [], - "name": "CONTRACT_NAME", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "CONTRACT_VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "CRA_SIGNER", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DOMAIN_SEPARATOR", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "ETH_ADDRESS", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "__assetReceiptRegister", - "outputs": [ - { - "internalType": "address", - "name": "assetContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "assetTokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "_marketRegistry", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "_tellerV2", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_discreteOrderId", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "address", - "name": "considerationToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "considerationIdentifier", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "considerationAmount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "offerer", - "type": "address" - }, - { - "internalType": "address", - "name": "zone", - "type": "address" - }, - { - "internalType": "address", - "name": "offerToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "offerIdentifier", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "offerAmount", - "type": "uint256" - }, - { - "internalType": "enum BasicOrderType", - "name": "basicOrderType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "startTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "zoneHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "salt", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "offererConduitKey", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "fulfillerConduitKey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "totalOriginalAdditionalRecipients", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "recipient", - "type": "address" - } - ], - "internalType": "struct AdditionalRecipient[]", - "name": "additionalRecipients", - "type": "tuple[]" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "internalType": "struct BasicOrderParameters", - "name": "_basicOrderParameters", - "type": "tuple" - } - ], - "name": "acceptDiscreteOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "allowedFeeRecipients", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_discreteOrderId", - "type": "uint256" - } - ], - "name": "cancelDiscreteOrder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "bidId", - "type": "uint256" - } - ], - "name": "claimNFTFromDefaultedLoan", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "bidId", - "type": "uint256" - } - ], - "name": "claimNFTFromRepaidLoan", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "cryptopunksEscrowBuyer", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "cryptopunksMarketAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "discreteOrderCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "discreteOrders", - "outputs": [ - { - "internalType": "address", - "name": "borrower", - "type": "address" - }, - { - "components": [ - { - "internalType": "address", - "name": "lender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "totalPurchasePrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "principal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "downPayment", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "duration", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "signatureExpiration", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "interestRate", - "type": "uint16" - }, - { - "internalType": "string", - "name": "metadataURI", - "type": "string" - }, - { - "internalType": "address", - "name": "referralAddress", - "type": "address" - } - ], - "internalType": "struct BNPLMarket.SubmitBidArgs", - "name": "submitBidArgs", - "type": "tuple" - }, - { - "internalType": "address", - "name": "assetContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "assetTokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "bidId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "escrowedTokensForLoan", - "outputs": [ - { - "internalType": "enum IBNPLMarket.TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "tokenClaimed", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "lender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "totalPurchasePrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "principal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "downPayment", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "duration", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "signatureExpiration", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "interestRate", - "type": "uint16" - }, - { - "internalType": "string", - "name": "metadataURI", - "type": "string" - }, - { - "internalType": "address", - "name": "referralAddress", - "type": "address" - } - ], - "internalType": "struct BNPLMarket.SubmitBidArgs", - "name": "_submitBidArgs", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "address", - "name": "considerationToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "considerationIdentifier", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "considerationAmount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "offerer", - "type": "address" - }, - { - "internalType": "address", - "name": "zone", - "type": "address" - }, - { - "internalType": "address", - "name": "offerToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "offerIdentifier", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "offerAmount", - "type": "uint256" - }, - { - "internalType": "enum BasicOrderType", - "name": "basicOrderType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "startTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endTime", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "zoneHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "salt", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "offererConduitKey", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "fulfillerConduitKey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "totalOriginalAdditionalRecipients", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address payable", - "name": "recipient", - "type": "address" - } - ], - "internalType": "struct AdditionalRecipient[]", - "name": "additionalRecipients", - "type": "tuple[]" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "internalType": "struct BasicOrderParameters", - "name": "_basicOrderParameters", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "_signature", - "type": "bytes" - } - ], - "name": "execute", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "executedSignatures", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getMarketRegistry", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTellerV2", - "outputs": [ - { - "internalType": "contract TellerV2", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "marketId", - "type": "uint256" - } - ], - "name": "getTellerV2MarketOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "lender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "totalPurchasePrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "principal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "downPayment", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "duration", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "signatureExpiration", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "interestRate", - "type": "uint16" - }, - { - "internalType": "string", - "name": "metadataURI", - "type": "string" - }, - { - "internalType": "address", - "name": "referralAddress", - "type": "address" - } - ], - "internalType": "struct BNPLMarket.SubmitBidArgs", - "name": "_submitBidArgs", - "type": "tuple" - }, - { - "internalType": "address", - "name": "assetContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "assetTokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "assetQuantity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "totalPurchasePrice", - "type": "uint256" - }, - { - "internalType": "address", - "name": "paymentToken", - "type": "address" - } - ], - "name": "getTypeHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_cryptopunksMarketAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "_seaportEscrowBuyer", - "type": "address" - }, - { - "internalType": "address", - "name": "_cryptopunksEscrowBuyer", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "forwarder", - "type": "address" - } - ], - "name": "isTrustedForwarder", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "marketId", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_cryptopunksMarketAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "_seaportEscrowBuyer", - "type": "address" - }, - { - "internalType": "address", - "name": "_cryptopunksEscrowBuyer", - "type": "address" - } - ], - "name": "onUpgrade", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "domainSeparator", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "typeHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "_signature", - "type": "bytes" - } - ], - "name": "recoverSignature", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "referralRewardPercent", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "seaportEscrowBuyer", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_feeRecipient", - "type": "address" - }, - { - "internalType": "bool", - "name": "_allowed", - "type": "bool" - } - ], - "name": "setAllowedFeeRecipient", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "_pct", - "type": "uint16" - } - ], - "name": "setReferralRewardPercent", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "lender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "totalPurchasePrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "principal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "downPayment", - "type": "uint256" - }, - { - "internalType": "uint32", - "name": "duration", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "signatureExpiration", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "interestRate", - "type": "uint16" - }, - { - "internalType": "string", - "name": "metadataURI", - "type": "string" - }, - { - "internalType": "address", - "name": "referralAddress", - "type": "address" - } - ], - "internalType": "struct BNPLMarket.SubmitBidArgs", - "name": "_submitBidArgs", - "type": "tuple" - }, - { - "internalType": "address", - "name": "_assetContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_assetTokenId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_quantity", - "type": "uint256" - } - ], - "name": "submitDiscreteOrder", - "outputs": [ - { - "internalType": "uint256", - "name": "discreteOrderId", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "tokenAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "enum IBNPLMarket.TokenType", - "name": "tokenType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferLegacyAssetToEscrow", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "tokenAddresses", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "tokenIds", - "type": "uint256[]" - }, - { - "internalType": "enum IBNPLMarket.TokenType[]", - "name": "tokenTypes", - "type": "uint8[]" - }, - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "name": "transferLegacyAssetsArrayToEscrow", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "upgradedToVersion", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "wethAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"_tellerV2","type":"address"},{"internalType":"address","name":"_marketRegistry","type":"address"},{"internalType":"address","name":"_wethAddress","type":"address"},{"internalType":"address","name":"_trustedForwarder","type":"address"},{"internalType":"address","name":"_craSigner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"lender","type":"address"}],"name":"AssetPurchaseWithLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"tokenContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ClaimedAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"marketId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_feeRecipient","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"SetAllowedFeeRecipient","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"marketId","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"pct","type":"uint16"}],"name":"SetReferralReward","type":"event"},{"inputs":[],"name":"CONTRACT_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CONTRACT_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CRA_SIGNER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_marketRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_tellerV2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"allowedFeeRecipients","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"claimNFTFromDefaultedLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"claimNFTFromRepaidLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cryptopunksEscrowBuyer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cryptopunksMarketAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"escrowedTokensForLoan","outputs":[{"internalType":"enum IBNPLMarket.TokenType","name":"tokenType","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"tokenClaimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"totalPurchasePrice","type":"uint256"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"downPayment","type":"uint256"},{"internalType":"uint32","name":"duration","type":"uint32"},{"internalType":"uint32","name":"signatureExpiration","type":"uint32"},{"internalType":"uint16","name":"interestRate","type":"uint16"},{"internalType":"string","name":"metadataURI","type":"string"},{"internalType":"address","name":"referralAddress","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"internalType":"struct BNPLMarket.SubmitBidArgs","name":"_submitBidArgs","type":"tuple"},{"components":[{"internalType":"address","name":"considerationToken","type":"address"},{"internalType":"uint256","name":"considerationIdentifier","type":"uint256"},{"internalType":"uint256","name":"considerationAmount","type":"uint256"},{"internalType":"address payable","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"internalType":"address","name":"offerToken","type":"address"},{"internalType":"uint256","name":"offerIdentifier","type":"uint256"},{"internalType":"uint256","name":"offerAmount","type":"uint256"},{"internalType":"enum BasicOrderType","name":"basicOrderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"offererConduitKey","type":"bytes32"},{"internalType":"bytes32","name":"fulfillerConduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalAdditionalRecipients","type":"uint256"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct AdditionalRecipient[]","name":"additionalRecipients","type":"tuple[]"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct BasicOrderParameters","name":"_basicOrderParameters","type":"tuple"},{"internalType":"bytes","name":"_signatureFromBorrower","type":"bytes"},{"internalType":"bytes","name":"_signatureFromLender","type":"bytes"}],"name":"executeUsingOffchainSignatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"executedSignatures","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTellerV2","outputs":[{"internalType":"contract TellerV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"getTellerV2MarketOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"totalPurchasePrice","type":"uint256"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"downPayment","type":"uint256"},{"internalType":"uint32","name":"duration","type":"uint32"},{"internalType":"uint32","name":"signatureExpiration","type":"uint32"},{"internalType":"uint16","name":"interestRate","type":"uint16"},{"internalType":"string","name":"metadataURI","type":"string"},{"internalType":"address","name":"referralAddress","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"internalType":"struct BNPLMarket.SubmitBidArgs","name":"_submitBidArgs","type":"tuple"},{"internalType":"address","name":"assetContractAddress","type":"address"},{"internalType":"uint256","name":"assetTokenId","type":"uint256"},{"internalType":"uint256","name":"assetQuantity","type":"uint256"},{"internalType":"uint256","name":"totalPurchasePrice","type":"uint256"},{"internalType":"address","name":"paymentToken","type":"address"}],"name":"getTypeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_cryptopunksMarketAddress","type":"address"},{"internalType":"address","name":"_seaportEscrowBuyer","type":"address"},{"internalType":"address","name":"_cryptopunksEscrowBuyer","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"seaportEscrowBuyer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_marketId","type":"uint256"},{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setAllowedFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_marketId","type":"uint256"},{"internalType":"uint16","name":"_pct","type":"uint16"}],"name":"setReferralRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradedToVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wethAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/tasks/callExecuteWithOffchain.ts b/tasks/callExecuteWithOffchain.ts index 2d32c68..8125317 100644 --- a/tasks/callExecuteWithOffchain.ts +++ b/tasks/callExecuteWithOffchain.ts @@ -217,7 +217,7 @@ export async function callExecuteWithOffchain(): Promise { /* let unsignedTx = await bnplContractInstance .populateTransaction - .executeWithOffchainSignatures( + .executeUsingOffchainSignatures( formattedSubmitBidArgs, basicOrderParams, borrowerSignature, diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index bb2882a..d541b73 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -116,6 +116,8 @@ export async function executeReservoirOrder(): Promise { } + console.log({bnplConfig}) + let rpcProvider = new providers.JsonRpcProvider( rpcURI ) let tellerV2Instance = new Contract(tellerV2Config.address,tellerV2Config.abi, rpcProvider) @@ -125,6 +127,10 @@ export async function executeReservoirOrder(): Promise { const borrowerWallet = new Wallet(borrowerPrivateKey) + + const borrowerAddress = borrowerWallet.address + + const lenderWallet = new Wallet(lenderPrivateKey) const lenderAddress = lenderWallet.address @@ -143,16 +149,26 @@ export async function executeReservoirOrder(): Promise { BigNumber.from(5) //protocol fee ).toString() + + const secondsNow = Math.floor(Date.now()/1000) + + const signatureExpiration = secondsNow + 90*60*1000 + let submitBidArgs:SubmitBidArgs = { + borrower: borrowerAddress, + lender: lenderAddress, + + totalPurchasePrice, principal, downPayment, duration: '28000', - signatureExpiration: (Date.now() + 90*60*1000).toString(), + signatureExpiration: signatureExpiration.toString(), interestRate: '300', - referralAddress: ethers.constants.AddressZero, + metadataURI: 'ipfs://', + referralAddress: ethers.constants.AddressZero, marketId } @@ -211,14 +227,15 @@ export async function executeReservoirOrder(): Promise { let unsignedTx = await bnplContractInstance .populateTransaction - .executeWithOffchainSignatures( + .executeUsingOffchainSignatures( formattedSubmitBidArgs, basicOrderParams, borrowerSignature, lenderSignature - , {value, gasLimit, gasPrice} ) + , {from: borrowerAddress, value, gasLimit, gasPrice} ) + console.log('made unsigned tx') let response = await borrowerWallet.sendTransaction(unsignedTx); console.log('response',response) From feb6966e67c35de93ad9251f402d1dca9f45fcc9 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 15:20:56 -0500 Subject: [PATCH 05/10] cannot hexlify --- tasks/executeReservoirOrder.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index d541b73..118a3b6 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -198,6 +198,7 @@ export async function executeReservoirOrder(): Promise { //fix it for now to remove referral and sig expir let formattedSubmitBidArgs:SubmitBidArgs = { + borrower: borrowerAddress, lender: lenderAddress, totalPurchasePrice: submitBidArgs.totalPurchasePrice, principal: submitBidArgs.principal, @@ -212,8 +213,8 @@ export async function executeReservoirOrder(): Promise { - console.log({basicOrderParams}) - console.log({formattedSubmitBidArgs}) + console.log(JSON.stringify(basicOrderParams)) + console.log(JSON.stringify(formattedSubmitBidArgs)) //Set price to 1 Gwei @@ -223,7 +224,9 @@ export async function executeReservoirOrder(): Promise { - let value:BigNumber = BigNumber.from(submitBidArgs.downPayment) + let value:BigNumber = BigNumber.from(submitBidArgs.downPayment) + + console.log({value}) let unsignedTx = await bnplContractInstance .populateTransaction @@ -232,7 +235,7 @@ export async function executeReservoirOrder(): Promise { basicOrderParams, borrowerSignature, lenderSignature - , {from: borrowerAddress, value, gasLimit, gasPrice} ) + , { value, gasLimit, gasPrice} ) console.log('made unsigned tx') From 40d639270d088ed1e096aa232ff16e860168f32d Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 15:25:51 -0500 Subject: [PATCH 06/10] tx working --- lib/bnpl-helper.ts | 23 +++-------------------- tasks/executeReservoirOrder.ts | 16 +++++++--------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/lib/bnpl-helper.ts b/lib/bnpl-helper.ts index cb5faaf..4afb3c8 100644 --- a/lib/bnpl-helper.ts +++ b/lib/bnpl-helper.ts @@ -92,25 +92,8 @@ export function buildExecuteParams(inputData:CraResponse ): ExecuteParams { - const signature = signOffchainOffer(signConfig) + const signature = await signOffchainOffer(signConfig) - - - /* let signatureVersion = 3 - - let signatureResponse = await craSign( - submitBidArgs, - basicOrderParams, - chainId, - signatureVersion, - implementationContractAddress, - wallet, - true) - - - if(signatureResponse.success){ - return signatureResponse.data - } - - return undefined */ + return signature + } \ No newline at end of file diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index 118a3b6..3dc4c97 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -125,7 +125,7 @@ export async function executeReservoirOrder(): Promise { - const borrowerWallet = new Wallet(borrowerPrivateKey) + const borrowerWallet = new Wallet(borrowerPrivateKey, rpcProvider) const borrowerAddress = borrowerWallet.address @@ -206,14 +206,14 @@ export async function executeReservoirOrder(): Promise { duration: submitBidArgs.duration, signatureExpiration: submitBidArgs.signatureExpiration, interestRate:submitBidArgs.interestRate, - referralAddress: submitBidArgs.referralAddress, metadataURI: submitBidArgs.metadataURI , + referralAddress: submitBidArgs.referralAddress, marketId } - console.log(JSON.stringify(basicOrderParams)) + console.log(JSON.stringify(basicOrderParamsFormatted)) console.log(JSON.stringify(formattedSubmitBidArgs)) @@ -223,19 +223,17 @@ export async function executeReservoirOrder(): Promise { var gasLimit = utils.hexlify(10_000_000); - - let value:BigNumber = BigNumber.from(submitBidArgs.downPayment) - - console.log({value}) + + console.log({borrowerSignature}) let unsignedTx = await bnplContractInstance .populateTransaction .executeUsingOffchainSignatures( formattedSubmitBidArgs, - basicOrderParams, + basicOrderParamsFormatted, borrowerSignature, lenderSignature - , { value, gasLimit, gasPrice} ) + , { gasLimit, gasPrice} ) console.log('made unsigned tx') From 3b489200cc4f213023f41d7f904c863d8b1cd4cf Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 15:39:29 -0500 Subject: [PATCH 07/10] so close but invalid sig --- tasks/executeReservoirOrder.ts | 227 +++++---------------------------- 1 file changed, 29 insertions(+), 198 deletions(-) diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index 3dc4c97..6e0822e 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -4,6 +4,7 @@ +import { recoverSignerOfOffchainOffer } from '@clarity-credit/anpl-sdk' import axios from 'axios' import { toChecksumAddress } from 'ethereumjs-util' import {Contract, Wallet, providers, utils, BigNumber, ethers} from 'ethers' @@ -115,7 +116,7 @@ export async function executeReservoirOrder(): Promise { signature: basicOrderParams.signature } - + console.log({rpcURI}) console.log({bnplConfig}) let rpcProvider = new providers.JsonRpcProvider( rpcURI ) @@ -131,7 +132,7 @@ export async function executeReservoirOrder(): Promise { const borrowerAddress = borrowerWallet.address - const lenderWallet = new Wallet(lenderPrivateKey) + const lenderWallet = new Wallet(lenderPrivateKey,rpcProvider) const lenderAddress = lenderWallet.address @@ -181,20 +182,7 @@ export async function executeReservoirOrder(): Promise { verifyingContract: implementationContractAddress } - let borrowerSignature = await generateBNPLOrderSignature( - submitBidArgs, - basicOrderParamsFormatted, - domainData, - borrowerWallet, - ) - - let lenderSignature = await generateBNPLOrderSignature( - submitBidArgs, - basicOrderParamsFormatted, - domainData, - lenderWallet, - ) - + //fix it for now to remove referral and sig expir let formattedSubmitBidArgs:SubmitBidArgs = { @@ -213,6 +201,30 @@ export async function executeReservoirOrder(): Promise { + let borrowerSignature = await generateBNPLOrderSignature( + formattedSubmitBidArgs, + basicOrderParamsFormatted, + domainData, + borrowerWallet, + ) + + let lenderSignature = await generateBNPLOrderSignature( + formattedSubmitBidArgs, + basicOrderParamsFormatted, + domainData, + lenderWallet, + ) + + + let recoveredSigner = recoverSignerOfOffchainOffer( + formattedSubmitBidArgs, + basicOrderParamsFormatted, + domainData, + lenderSignature + ) + + console.log({recoveredSigner}) + console.log(JSON.stringify(basicOrderParamsFormatted)) console.log(JSON.stringify(formattedSubmitBidArgs)) @@ -224,7 +236,7 @@ export async function executeReservoirOrder(): Promise { - console.log({borrowerSignature}) + console.log({borrowerSignature},{lenderSignature}) let unsignedTx = await bnplContractInstance .populateTransaction @@ -241,188 +253,7 @@ export async function executeReservoirOrder(): Promise { let response = await borrowerWallet.sendTransaction(unsignedTx); console.log('response',response) - - - /* - - let rpcProvider = new providers.JsonRpcProvider( rpcURI ) - - let tellerV2Instance = new Contract(tellerV2Config.address,tellerV2Config.abi, rpcProvider) - let bnplContractInstance = new Contract(bnplConfig.address,bnplConfig.abi,rpcProvider) - - - let signatureVersion = await readSignatureVersionFromBNPLMarketContract( bnplContractInstance ) - - let craInputs = { - asset_contract_address:tokenInputData.tokenAddress, - token_id:tokenInputData.tokenId, - quantity: tokenInputData.tokenQuantity, - - chain_id:tokenInputData.chainId, - signature_version: signatureVersion - } - - // let craResponse = await performCraRequest( craInputs ) - let craResponse = {success:true, data: craResponseSample , error:'none'} - - console.log('meep', craResponse) - - - if(!craResponse.success || !craResponse.data) throw new Error('cra error '.concat(craResponse.error.toString())) - - - let executeParams = craResponse.data - - if(typeof(executeParams.submitBidArgs.metadataURI) == 'undefined'){ - executeParams.submitBidArgs.metadataURI = "ipfs://" - } - - - if(!borrowerPrivateKey) throw new Error('Missing borrowerPrivateKey') - - - if(!lenderPrivateKey) throw new Error('Missing lenderPrivateKey') - - let borrowerWallet = new Wallet(borrowerPrivateKey).connect(rpcProvider) - - let lenderWallet = new Wallet(lenderPrivateKey).connect(rpcProvider) - - console.log(`calling execute using account ${borrowerWallet.address}`) - - - const submitBidArgs:SubmitBidArgs = executeParams.submitBidArgs - - - submitBidArgs.borrower = borrowerWallet.address - submitBidArgs.lender = borrowerWallet.address - - let value:BigNumber = BigNumber.from(submitBidArgs.downPayment) - - let lenderAddress = lenderWallet.address - - let basicOrderParams:BasicOrderParams = executeParams.basicOrderParams - - if(!basicOrderParams.offererConduitKey){ - throw new Error('Missing offererConduitKey') - } - - */ - - - /* - const chainId = tokenInputData.chainId - - - - // find implementation address via proxy admin - - let bnplContractProxyAddress = contractsConfig.BNPLContract.address - - const proxyAdminContract = new Contract( contractsConfig.proxyAdmin.address, ProxyAdminInterface, rpcProvider ) - let implementationContractAddress = await proxyAdminContract.getProxyImplementation( bnplContractProxyAddress ) - - implementationContractAddress = toChecksumAddress(implementationContractAddress) - - if(typeof(implementationContractAddress) == 'undefined'){ - return {success:false, error:"Could not get implementation address"} - } - - - - let lenderSignature = await generateBNPLOrderSignature( - submitBidArgs, - basicOrderParams, - lenderWallet, - chainId, - implementationContractAddress - ) - - let borrowerSignature = await generateBNPLOrderSignature( - submitBidArgs, - basicOrderParams, - borrowerWallet, - chainId, - implementationContractAddress - ) - - - - - let lenderHasApproved = await tellerV2Instance.hasApprovedMarketForwarder(executeConfig.marketplaceId, bnplContractInstance.address, lenderAddress) - console.log('lender has approved BNPL as forwarder: ',lenderHasApproved, lenderAddress) - - if(!lenderHasApproved) { - console.error(`ERROR: lender ${lenderAddress} has not approved bnpl as forwarder `) - return - } - - - let borrowerHasApproved = await tellerV2Instance.hasApprovedMarketForwarder(executeConfig.marketplaceId, bnplContractInstance.address, lenderAddress) - console.log('lender has approved BNPL as forwarder: ',borrowerHasApproved, lenderAddress) - - if(!borrowerHasApproved) { - console.error(`ERROR: lender ${lenderAddress} has not approved bnpl as forwarder `) - return - } - - - - - //fix it for now to remove referral and sig expir - let formattedSubmitBidArgs:SubmitBidArgs = { - lender: lenderAddress, - totalPurchasePrice: submitBidArgs.totalPurchasePrice, - principal: submitBidArgs.principal, - downPayment: submitBidArgs.downPayment, - duration: submitBidArgs.duration, - signatureExpiration: submitBidArgs.signatureExpiration, - interestRate:submitBidArgs.interestRate, - referralAddress: submitBidArgs.referralAddress, - metadataURI: submitBidArgs.metadataURI , - } - - - console.log('passing in params', - formattedSubmitBidArgs, - basicOrderParams , - borrowerSignature, - lenderSignature - ) - // basicOrderParams.offererConduitKey = "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000" - //basicOrderParams.signature= "0x5c8a6fd29db37f9b53fcf1cce21c62e68ca0684c28e0320cb27e8568210baf37644e1258430b698cfe533334621ddd82a12e46dd184823ab318b8ee6ff0fa2dd1c" - - //this address needs to approve the forwarder on tellerv2 - // lenderAddress = "0xF4dAb24C52b51cB69Ab62cDE672D3c9Df0B39681" - - //Set price to 1 Gwei - let gasPrice = utils.hexlify(8_000_000_000); - //Set max gas limit to 4M - var gasLimit = utils.hexlify(10_000_000); - - - if((basicOrderParams.basicOrderType) > 22){ - throw new Error('invalid basic order type') - } - - - */ - - /* let unsignedTx = await bnplContractInstance - .populateTransaction - .executeWithOffchainSignatures( - formattedSubmitBidArgs, - basicOrderParams, - borrowerSignature, - lenderSignature - - , {value, gasLimit, gasPrice} ) - - - - let response = await borrowerWallet.sendTransaction(unsignedTx); - console.log('response',response)*/ - return true } From 86cbdf4e0236ca630e87535ad2ff60bbaef87c25 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 15:47:50 -0500 Subject: [PATCH 08/10] sig valid failed --- tasks/executeReservoirOrder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index 6e0822e..eecc8f8 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -173,7 +173,7 @@ export async function executeReservoirOrder(): Promise { marketId } - const implementationContractAddress = "0x3bf7f0d0fa47f2101f67bd530f1be7ad05d90321" + const implementationContractAddress = "0x3bf7F0D0FA47F2101f67bd530F1bE7aD05D90321" const domainData:DomainData = { name: 'Teller_BNPL_Market', From 1d1bc489ad6f702e38b90c8c5412936a183a8c4e Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 16:14:56 -0500 Subject: [PATCH 09/10] SUCCESS --- tasks/executeReservoirOrder.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index eecc8f8..ed5b2f2 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -138,7 +138,10 @@ export async function executeReservoirOrder(): Promise { - const totalPurchasePrice = basicOrderParams.considerationAmount + + const considerationAmount = BigNumber.from( basicOrderParams.considerationAmount ) + const additionalAmount = BigNumber.from( basicOrderParams.additionalRecipients[0].amount) + const totalPurchasePrice = considerationAmount.add(additionalAmount).toString() const downPayment = BigNumber.from(totalPurchasePrice).div(2).toString() @@ -173,7 +176,8 @@ export async function executeReservoirOrder(): Promise { marketId } - const implementationContractAddress = "0x3bf7F0D0FA47F2101f67bd530F1bE7aD05D90321" + const implementationContractAddress = bnplConfig.address + //"0x3bf7F0D0FA47F2101f67bd530F1bE7aD05D90321" const domainData:DomainData = { name: 'Teller_BNPL_Market', From 00eaff89159afd8c19f247a97acac746400b01f9 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 18 Jan 2023 16:22:58 -0500 Subject: [PATCH 10/10] sum --- tasks/executeReservoirOrder.ts | 36 +++++++++++++++------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/tasks/executeReservoirOrder.ts b/tasks/executeReservoirOrder.ts index ed5b2f2..1bb3dc4 100644 --- a/tasks/executeReservoirOrder.ts +++ b/tasks/executeReservoirOrder.ts @@ -15,7 +15,7 @@ import { buildExecuteParams, calculateTotalPrice, generateBNPLOrderSignature, pe import { fetchReservoirOrderById, formatReservoirOrder } from '../lib/reservoir-helper' import { calculatePrincipalRequiredForBorrowerPayout } from '../lib/teller-v2-lending-helper' -import { AdditionalRecipient, BasicOrderParams, DomainData, ReservoirOrder, ReservoirOrderRawData, SubmitBidArgs } from '../lib/types' +import { AdditionalRecipient, AdditionalRecipientResponse, BasicOrderParams, DomainData, ReservoirOrder, ReservoirOrderRawData, SubmitBidArgs } from '../lib/types' require('dotenv').config() @@ -42,28 +42,11 @@ const bnplConfig = { const chainId = "1" const marketId = "6" - -/* - -const executeConfig = { - - marketplaceId: 2 - -} - -const craResponseSample = require('../test/data/sampleCraOutput.json') - -let tokenInputData = require('../data/tokenInputData.json') -let networkName = networkNameFromChainId( tokenInputData.chainId ) -let contractsConfig = require('../data/contractsConfig.json')[networkName] - -const ProxyAdminInterface = require('../abi/OpenZeppelinTransparentProxyAdmin.abi.json') -*/ /* -Test w tenderly test RPC ? +Test w tenderly test RPC */ @@ -140,7 +123,7 @@ export async function executeReservoirOrder(): Promise { const considerationAmount = BigNumber.from( basicOrderParams.considerationAmount ) - const additionalAmount = BigNumber.from( basicOrderParams.additionalRecipients[0].amount) + const additionalAmount = calculateTotalAdditionalAmount( basicOrderParams.additionalRecipients ) const totalPurchasePrice = considerationAmount.add(additionalAmount).toString() const downPayment = BigNumber.from(totalPurchasePrice).div(2).toString() @@ -265,3 +248,16 @@ export async function executeReservoirOrder(): Promise { + + + + function calculateTotalAdditionalAmount( additionalRecipients: AdditionalRecipientResponse[] ) : BigNumber { + + let result = BigNumber.from(0) + + additionalRecipients.map( (r:any) => result = result.add(r.amount)) + + return result + + + } \ No newline at end of file