diff --git a/bulla-contracts/abis/BullaSwap.json b/bulla-contracts/abis/BullaSwap.json new file mode 100644 index 0000000..2ebb307 --- /dev/null +++ b/bulla-contracts/abis/BullaSwap.json @@ -0,0 +1,501 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOrder", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSigner", + "type": "error" + }, + { + "inputs": [], + "name": "NewOwnerIsZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "NoHandoverRequest", + "type": "error" + }, + { + "inputs": [], + "name": "OrderExpired", + "type": "error" + }, + { + "inputs": [], + "name": "SignerAllowanceLow", + "type": "error" + }, + { + "inputs": [], + "name": "SignerBalanceLow", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "signerToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "signerAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "senderWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "senderToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "senderAmount", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IBullaSwap.OrderERC20", + "name": "order", + "type": "tuple" + } + ], + "name": "OrderCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "signerToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "signerAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "senderWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "senderToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "senderAmount", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IBullaSwap.OrderERC20", + "name": "order", + "type": "tuple" + } + ], + "name": "OrderExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "OwnershipHandoverCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "OwnershipHandoverRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "cancelOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "completeOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "signerToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "signerAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "senderWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "senderToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "senderAmount", + "type": "uint256" + } + ], + "internalType": "struct IBullaSwap.OrderERC20", + "name": "order", + "type": "tuple" + } + ], + "name": "createOrder", + "outputs": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + } + ], + "name": "executeOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "orderCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "orderExecuted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "orders", + "outputs": [ + { + "internalType": "uint256", + "name": "orderId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signerWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "signerToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "signerAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "senderWallet", + "type": "address" + }, + { + "internalType": "address", + "name": "senderToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "senderAmount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "result", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "ownershipHandoverExpiresAt", + "outputs": [ + { + "internalType": "uint256", + "name": "result", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "requestOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/bulla-contracts/config/sepolia.json b/bulla-contracts/config/sepolia.json index 405e2ec..981925c 100644 --- a/bulla-contracts/config/sepolia.json +++ b/bulla-contracts/config/sepolia.json @@ -9,5 +9,6 @@ "frendLend": { "address": "0x3E058834CE20A54F0755889c008D3fF62D33cE85", "startBlock": 5096555 }, "instantPayment": { "address": "0x1cD1A83C2965CB7aD55d60551877Eb390e9C3d7A", "startBlock": 5096555 }, "bullaFactoring": { "address": "0x0000000000000000000000000000000000000000", "startBlock": 6631476 }, - "bullaFactoringv2": { "address": "0xDF0fCe31285dcAB9124bF763AB9E5466723BeF35", "startBlock": 6812207 } + "bullaFactoringv2": { "address": "0xDF0fCe31285dcAB9124bF763AB9E5466723BeF35", "startBlock": 6812207 }, + "bullaSwap": { "address": "0xdf30BE5964a7E26b49551a430e85B51148A7b95E", "startBlock": 6982025 } } diff --git a/bulla-contracts/package.json b/bulla-contracts/package.json index e057011..2159279 100644 --- a/bulla-contracts/package.json +++ b/bulla-contracts/package.json @@ -4,7 +4,7 @@ "scripts": { "codegen": "yarn prep:sepolia && graph codegen", "build": "yarn prep:sepolia && graph codegen && graph build", - "test": "yarn && yarn prep:goerli && graph test -v 0.2.2", + "test": "rm -rf tests/.bin && yarn && yarn prep:goerli && graph test -v 0.2.2", "coverage": "yarn prep:goerli && graph test -- -c", "prep:mainnet": "mustache config/mainnet.json template.yaml > subgraph.yaml", "prep:goerli": "mustache config/goerli.json template.yaml > subgraph.yaml", diff --git a/bulla-contracts/schema.graphql b/bulla-contracts/schema.graphql index 9342856..2022670 100644 --- a/bulla-contracts/schema.graphql +++ b/bulla-contracts/schema.graphql @@ -286,6 +286,42 @@ type InvoiceReconciledEvent implements IEventLog & IPoolTransaction @entity { claim: Claim! } +type OrderERC20 @entity { + id: ID! # orderId + orderId: BigInt! + expiry: BigInt! + signerWallet: Bytes! #address + signerToken: Token! #address + signerAmount: BigInt! + senderWallet: Bytes! #address + senderToken: Token! #address + senderAmount: BigInt! +} + +type OrderCreatedEvent implements IEventLog @entity { + id: ID! + order: OrderERC20! + sender: Bytes! #address + signerWallet: Bytes! #address + eventName: String! + blockNumber: BigInt! + transactionHash: Bytes! + logIndex: BigInt! + timestamp: BigInt! +} + +type OrderExecutedEvent implements IEventLog @entity { + id: ID! + order: OrderERC20! + sender: Bytes! #address + signerWallet: Bytes! #address + eventName: String! + blockNumber: BigInt! + transactionHash: Bytes! + logIndex: BigInt! + timestamp: BigInt! +} + type LoanOfferedEvent implements IEventLog @entity { id: ID! loanId: String! @@ -448,6 +484,7 @@ type User @entity { financeEvents: [IEventLog!]! frendLendEvents: [IEventLog!]! factoringEvents: [IEventLog!]! + swapEvents: [IEventLog!]! } type PoolPnl @entity { diff --git a/bulla-contracts/src/functions/BullaSwap.ts b/bulla-contracts/src/functions/BullaSwap.ts new file mode 100644 index 0000000..f08ed3d --- /dev/null +++ b/bulla-contracts/src/functions/BullaSwap.ts @@ -0,0 +1,17 @@ +import { BigInt, ethereum } from "@graphprotocol/graph-ts"; +import { OrderCreated, OrderExecuted } from "../../generated/BullaSwap/BullaSwap"; +import { OrderCreatedEvent, OrderExecutedEvent } from "../../generated/schema"; + +export const getOrderCreatedEventId = (orderId: BigInt, event: ethereum.Event): string => + "OrderCreated-" + orderId.toString() + "-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); + +export const createOrderCreatedEvent = (orderId: BigInt, event: OrderCreated): OrderCreatedEvent => { + return new OrderCreatedEvent(getOrderCreatedEventId(orderId, event)); +}; + +export const getOrderExecutedEventId = (orderId: BigInt, event: ethereum.Event): string => + "OrderExecuted-" + orderId.toString() + "-" + event.transaction.hash.toHexString() + "-" + event.logIndex.toString(); + +export const createOrderExecutedEvent = (orderId: BigInt, event: OrderExecuted): OrderExecutedEvent => { + return new OrderExecutedEvent(getOrderExecutedEventId(orderId, event)); +}; diff --git a/bulla-contracts/src/mappings/BullaFactoring.ts b/bulla-contracts/src/mappings/BullaFactoring.ts index ed3b59f..db12f7b 100644 --- a/bulla-contracts/src/mappings/BullaFactoring.ts +++ b/bulla-contracts/src/mappings/BullaFactoring.ts @@ -255,18 +255,21 @@ export function handleInvoiceUnfactoredV2(event: InvoiceUnfactored): void { const targetFees = getTargetFeesAndTaxes(event.address, "v2", ev.invoiceId); const approvedInvoice = BullaFactoringv2.bind(event.address).approvedInvoices(ev.invoiceId); - const trueProcotolFee = targetFees[1] // targetProcotolFee - .times(ev.interestToCharge) - .div(targetFees[0]); // targetInterest + const trueProcotolFee = targetFees[0].isZero() + ? BigInt.fromI32(0) + : targetFees[1] // targetProcotolFee + .times(ev.interestToCharge) + .div(targetFees[0]); // targetInterest const trueTax = calculateTax(event.address, "v2", ev.interestToCharge); - const actualDays = (event.block.timestamp - .minus(approvedInvoice.getFundedTimestamp())) - .div(BigInt.fromI32(3600 * 24)); + const actualDays = event.block.timestamp.minus(approvedInvoice.getFundedTimestamp()).div(BigInt.fromI32(3600 * 24)); - const adminFee = - actualDays.times(BigInt.fromI32(approvedInvoice.getAdminFeeBps())).times(approvedInvoice.getTrueFaceValue()).div(BigInt.fromI32(365)).div(BigInt.fromI32(10_000)); + const adminFee = actualDays + .times(BigInt.fromI32(approvedInvoice.getAdminFeeBps())) + .times(approvedInvoice.getTrueFaceValue()) + .div(BigInt.fromI32(365)) + .div(BigInt.fromI32(10_000)); InvoiceUnfactoredEvent.invoiceId = underlyingClaim.id; InvoiceUnfactoredEvent.originalCreditor = ev.originalCreditor; @@ -602,7 +605,7 @@ export function handleActivePaidInvoicesReconciled(event: ActivePaidInvoicesReco originalCreditor.save(); pnlTotal = pnlTotal.plus(trueNetInterest); } - + const pool_pnl = getOrCreatePoolProfitAndLoss(event, pnlTotal); const price_per_share = getOrCreatePricePerShare(event, version); const historical_factoring_statistics = getOrCreateHistoricalFactoringStatistics(event, version); diff --git a/bulla-contracts/src/mappings/BullaSwap.ts b/bulla-contracts/src/mappings/BullaSwap.ts new file mode 100644 index 0000000..9ffc161 --- /dev/null +++ b/bulla-contracts/src/mappings/BullaSwap.ts @@ -0,0 +1,82 @@ +import { OrderCreated, OrderExecuted } from "../../generated/BullaSwap/BullaSwap"; +import { OrderERC20 } from "../../generated/schema"; +import { createOrderCreatedEvent, createOrderExecutedEvent } from "../functions/BullaSwap"; +import { getOrCreateToken, getOrCreateUser } from "../functions/common"; + +export function handleOrderCreated(event: OrderCreated): void { + const ev = event.params; + const orderId = ev.orderId; + + // Create the order entity + const order = new OrderERC20(orderId.toString()); + order.orderId = orderId; + order.expiry = ev.order.expiry; + order.signerWallet = ev.order.signerWallet; + order.signerToken = getOrCreateToken(ev.order.signerToken).id; + order.signerAmount = ev.order.signerAmount; + order.senderWallet = ev.order.senderWallet; + order.senderToken = getOrCreateToken(ev.order.senderToken).id; + order.senderAmount = ev.order.senderAmount; + order.save(); + + // Create the event entity + const orderCreatedEvent = createOrderCreatedEvent(orderId, event); + orderCreatedEvent.order = order.id; + orderCreatedEvent.sender = ev.sender; + orderCreatedEvent.signerWallet = ev.order.signerWallet; + orderCreatedEvent.eventName = "OrderCreated"; + orderCreatedEvent.blockNumber = event.block.number; + orderCreatedEvent.transactionHash = event.transaction.hash; + orderCreatedEvent.logIndex = event.logIndex; + orderCreatedEvent.timestamp = event.block.timestamp; + + // Update user entities + const sender = getOrCreateUser(ev.sender); + sender.swapEvents = sender.swapEvents ? sender.swapEvents.concat([orderCreatedEvent.id]) : [orderCreatedEvent.id]; + sender.save(); + + const signer = getOrCreateUser(ev.signerWallet); + signer.swapEvents = signer.swapEvents ? signer.swapEvents.concat([orderCreatedEvent.id]) : [orderCreatedEvent.id]; + signer.save(); + + orderCreatedEvent.save(); +} + +export function handleOrderExecuted(event: OrderExecuted): void { + const ev = event.params; + const orderId = ev.orderId; + + // Create the order entity + const order = new OrderERC20(orderId.toString()); + order.orderId = orderId; + order.expiry = ev.order.expiry; + order.signerWallet = ev.order.signerWallet; + order.signerToken = getOrCreateToken(ev.order.signerToken).id; + order.signerAmount = ev.order.signerAmount; + order.senderWallet = ev.order.senderWallet; + order.senderToken = getOrCreateToken(ev.order.senderToken).id; + order.senderAmount = ev.order.senderAmount; + order.save(); + + // Create the event entity + const orderExecutedEvent = createOrderExecutedEvent(orderId, event); + orderExecutedEvent.order = order.id; + orderExecutedEvent.sender = ev.sender; + orderExecutedEvent.signerWallet = ev.order.signerWallet; + orderExecutedEvent.eventName = "OrderExecuted"; + orderExecutedEvent.blockNumber = event.block.number; + orderExecutedEvent.transactionHash = event.transaction.hash; + orderExecutedEvent.logIndex = event.logIndex; + orderExecutedEvent.timestamp = event.block.timestamp; + + // Update user entities + const sender = getOrCreateUser(ev.sender); + sender.swapEvents = sender.swapEvents ? sender.swapEvents.concat([orderExecutedEvent.id]) : [orderExecutedEvent.id]; + sender.save(); + + const recipient = getOrCreateUser(ev.recipient); + recipient.swapEvents = recipient.swapEvents ? recipient.swapEvents.concat([orderExecutedEvent.id]) : [orderExecutedEvent.id]; + recipient.save(); + + orderExecutedEvent.save(); +} diff --git a/bulla-contracts/template.yaml b/bulla-contracts/template.yaml index 20613dd..2ec1ca8 100644 --- a/bulla-contracts/template.yaml +++ b/bulla-contracts/template.yaml @@ -310,4 +310,26 @@ dataSources: handler: handleSharesRedeemedWithAttachmentV2 - event: InvoiceImpaired(indexed uint256,uint256,uint256) handler: handleInvoiceImpairedV2 - file: ./src/mappings/BullaFactoring.ts \ No newline at end of file + file: ./src/mappings/BullaFactoring.ts + - kind: ethereum/contract + name: BullaSwap + network: {{ network }} + source: + address: "{{ bullaSwap.address }}" + startBlock: {{ bullaSwap.startBlock }} + abi: BullaSwap + mapping: + kind: ethereum/events + apiVersion: {{ apiVersion }} + language: wasm/assemblyscript + entities: + - BullaSwap + abis: + - name: BullaSwap + file: ./abis/BullaSwap.json + eventHandlers: + - event: OrderCreated(indexed uint256,indexed address,indexed address,(uint256,uint256,address,address,uint256,address,address,uint256)) + handler: handleOrderCreated + - event: OrderExecuted(indexed uint256,indexed address,indexed address,(uint256,uint256,address,address,uint256,address,address,uint256)) + handler: handleOrderExecuted + file: ./src/mappings/BullaSwap.ts diff --git a/bulla-contracts/tests/BullaSwap.test.ts b/bulla-contracts/tests/BullaSwap.test.ts new file mode 100644 index 0000000..e54540a --- /dev/null +++ b/bulla-contracts/tests/BullaSwap.test.ts @@ -0,0 +1,87 @@ +import { BigInt, log } from "@graphprotocol/graph-ts"; +import { assert, test } from "matchstick-as/assembly/index"; + +import { handleOrderCreated, handleOrderExecuted } from "../src/mappings/BullaSwap"; +import { ADDRESS_1, ADDRESS_2, ADDRESS_3, ADDRESS_4, afterEach, setupContracts } from "./helpers"; +import { newOrderCreatedEvent, newOrderExecutedEvent } from "./functions/BullaSwap.testtools"; +import { User } from "../generated/schema"; +import { getOrderCreatedEventId, getOrderExecutedEventId } from "../src/functions/BullaSwap"; + +test("it handles OrderCreated event", () => { + const orderId = BigInt.fromI32(3); + const expiry = BigInt.fromI32(100); + const signerWallet = ADDRESS_1; + const signerToken = ADDRESS_2; + const signerAmount = BigInt.fromI32(10000); + const senderWallet = ADDRESS_3; + const senderToken = ADDRESS_4; + const senderAmount = BigInt.fromI32(10000); + + setupContracts(); + const signerUser = new User(signerWallet.toHexString()); + signerUser.swapEvents = []; + signerUser.save(); + + const senderUser = new User(senderWallet.toHexString()); + senderUser.swapEvents = []; + senderUser.save(); + + const timestamp = BigInt.fromI32(100); + const blockNum = BigInt.fromI32(100); + + const orderCreatedEvent = newOrderCreatedEvent(orderId, signerWallet, signerToken, signerAmount, senderWallet, senderToken, senderAmount, expiry); + orderCreatedEvent.block.timestamp = timestamp; + orderCreatedEvent.block.number = blockNum; + + handleOrderCreated(orderCreatedEvent); + + const orderCreatedEventId = getOrderCreatedEventId(orderId, orderCreatedEvent); + assert.fieldEquals("OrderCreatedEvent", orderCreatedEventId, "signerWallet", signerWallet.toHexString()); + assert.fieldEquals("OrderCreatedEvent", orderCreatedEventId, "sender", senderWallet.toHexString()); + assert.fieldEquals("OrderERC20", orderId.toString(), "orderId", orderId.toString()); + + log.info("✅ should create a OrderCreated event", []); + + afterEach(); +}); + +test("it handles OrderExecuted event", () => { + const orderId = BigInt.fromI32(3); + const expiry = BigInt.fromI32(100); + const signerWallet = ADDRESS_1; + const signerToken = ADDRESS_2; + const signerAmount = BigInt.fromI32(10000); + const senderWallet = ADDRESS_3; + const senderToken = ADDRESS_4; + const senderAmount = BigInt.fromI32(10000); + + setupContracts(); + const signerUser = new User(signerWallet.toHexString()); + signerUser.swapEvents = []; + signerUser.save(); + + const senderUser = new User(senderWallet.toHexString()); + senderUser.swapEvents = []; + senderUser.save(); + + const timestamp = BigInt.fromI32(100); + const blockNum = BigInt.fromI32(100); + + const orderExecutedEvent = newOrderExecutedEvent(orderId, signerWallet, signerToken, signerAmount, senderWallet, senderToken, senderAmount, expiry); + orderExecutedEvent.block.timestamp = timestamp; + orderExecutedEvent.block.number = blockNum; + + handleOrderExecuted(orderExecutedEvent); + + const orderExecutedEventId = getOrderExecutedEventId(orderId, orderExecutedEvent); + assert.fieldEquals("OrderExecutedEvent", orderExecutedEventId, "signerWallet", signerWallet.toHexString()); + assert.fieldEquals("OrderExecutedEvent", orderExecutedEventId, "sender", senderWallet.toHexString()); + assert.fieldEquals("OrderERC20", orderId.toString(), "orderId", orderId.toString()); + + log.info("✅ should create a OrderExecuted event", []); + + afterEach(); +}); + +// exporting for test coverage +export { handleOrderCreated, handleOrderExecuted }; diff --git a/bulla-contracts/tests/functions/BullaSwap.testtools.ts b/bulla-contracts/tests/functions/BullaSwap.testtools.ts new file mode 100644 index 0000000..0291f8e --- /dev/null +++ b/bulla-contracts/tests/functions/BullaSwap.testtools.ts @@ -0,0 +1,98 @@ +import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; +import { newMockEvent } from "matchstick-as"; +import { OrderCreated, OrderExecuted } from "../../generated/BullaSwap/BullaSwap"; +import { MOCK_BULLA_SWAP_ADDRESS } from "../helpers"; + +/// @NOTICE: event parameters should be in the same order as the event declaration in the contract + +export function newOrderCreatedEvent( + orderId: BigInt, + signerWallet: Address, + signerToken: Address, + signerAmount: BigInt, + senderWallet: Address, + senderToken: Address, + senderAmount: BigInt, + expiry: BigInt +): OrderCreated { + const mockEvent = newMockEvent(); + const orderCreatedEvent = new OrderCreated( + mockEvent.address, + mockEvent.logIndex, + mockEvent.transactionLogIndex, + mockEvent.logType, + mockEvent.block, + mockEvent.transaction, + mockEvent.parameters, + mockEvent.receipt + ); + + orderCreatedEvent.address = MOCK_BULLA_SWAP_ADDRESS; + orderCreatedEvent.parameters = new Array(); + orderCreatedEvent.parameters.push(new ethereum.EventParam("orderId", ethereum.Value.fromUnsignedBigInt(orderId))); + orderCreatedEvent.parameters.push(new ethereum.EventParam("sender", ethereum.Value.fromAddress(senderWallet))); + orderCreatedEvent.parameters.push(new ethereum.EventParam("signerWallet", ethereum.Value.fromAddress(signerWallet))); + + const orderTupleArray = [ + ethereum.Value.fromUnsignedBigInt(orderId), + ethereum.Value.fromUnsignedBigInt(expiry), + ethereum.Value.fromAddress(signerWallet), + ethereum.Value.fromAddress(signerToken), + ethereum.Value.fromUnsignedBigInt(signerAmount), + ethereum.Value.fromAddress(senderWallet), + ethereum.Value.fromAddress(senderToken), + ethereum.Value.fromUnsignedBigInt(senderAmount) + ]; + + const orderTuple: ethereum.Tuple = changetype(orderTupleArray); + + orderCreatedEvent.parameters.push(new ethereum.EventParam("order", ethereum.Value.fromTuple(orderTuple))); + + return orderCreatedEvent; +} + +export function newOrderExecutedEvent( + orderId: BigInt, + signerWallet: Address, + signerToken: Address, + signerAmount: BigInt, + senderWallet: Address, + senderToken: Address, + senderAmount: BigInt, + expiry: BigInt +): OrderExecuted { + const mockEvent = newMockEvent(); + const orderExecutedEvent = new OrderExecuted( + mockEvent.address, + mockEvent.logIndex, + mockEvent.transactionLogIndex, + mockEvent.logType, + mockEvent.block, + mockEvent.transaction, + mockEvent.parameters, + mockEvent.receipt + ); + + orderExecutedEvent.address = MOCK_BULLA_SWAP_ADDRESS; + orderExecutedEvent.parameters = new Array(); + orderExecutedEvent.parameters.push(new ethereum.EventParam("orderId", ethereum.Value.fromUnsignedBigInt(orderId))); + orderExecutedEvent.parameters.push(new ethereum.EventParam("sender", ethereum.Value.fromAddress(senderWallet))); + orderExecutedEvent.parameters.push(new ethereum.EventParam("recipient", ethereum.Value.fromAddress(signerWallet))); + + const orderTupleArray = [ + ethereum.Value.fromUnsignedBigInt(orderId), + ethereum.Value.fromUnsignedBigInt(expiry), + ethereum.Value.fromAddress(signerWallet), + ethereum.Value.fromAddress(signerToken), + ethereum.Value.fromUnsignedBigInt(signerAmount), + ethereum.Value.fromAddress(senderWallet), + ethereum.Value.fromAddress(senderToken), + ethereum.Value.fromUnsignedBigInt(senderAmount) + ]; + + const orderTuple: ethereum.Tuple = changetype(orderTupleArray); + + orderExecutedEvent.parameters.push(new ethereum.EventParam("order", ethereum.Value.fromTuple(orderTuple))); + + return orderExecutedEvent; +} diff --git a/bulla-contracts/tests/helpers.ts b/bulla-contracts/tests/helpers.ts index 5ce5c85..c0d37c4 100644 --- a/bulla-contracts/tests/helpers.ts +++ b/bulla-contracts/tests/helpers.ts @@ -24,10 +24,12 @@ export const MOCK_SAFE_ADDRESS = Address.fromString("0x2270B1f2996327eB772351ee8 export const MOCK_SAFE_MODULE_ADDRESS = Address.fromString("0x70b841D46d227D458D9396e0c90A961e2B9D7a63"); export const MOCK_BULLA_TOKEN_ADDRESS = Address.fromString("0x90104Ff9aCd8EDB22BD5D030a11A1c2c66d95142"); export const MOCK_BULLA_FACTORING_ADDRESS = Address.fromString("0xd33abDe96F344FDe00B65650c8f68875D4c9e18A"); +export const MOCK_BULLA_SWAP_ADDRESS = Address.fromString("0x90104Ff9aCd8EDB22BD5D030a11A1c2c66d95142"); export const ADDRESS_ZERO = Address.fromString(addressZeroString); export const ADDRESS_1 = Address.fromString("0xb8c18E036d46c5FB94d7DeBaAeD92aFabe65EE61"); export const ADDRESS_2 = Address.fromString("0xe2B28b58cc5d34872794E861fd1ba1982122B907"); export const ADDRESS_3 = Address.fromString("0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); +export const ADDRESS_4 = Address.fromString("0xd8da6bf26964af9d7eed9e03e53415d37aa96017"); export const FEE_RECEIVER = ADDRESS_1; export const FEE_BPS = BigInt.fromU64(5); @@ -48,6 +50,13 @@ export const setupContracts = (): void => { createMockedFunction(MOCK_BULLA_TOKEN_ADDRESS, "decimals", "decimals():(uint8)").returns([ethereum.Value.fromI32(18)]); createMockedFunction(MOCK_BULLA_TOKEN_ADDRESS, "symbol", "symbol():(string)").returns([ethereum.Value.fromString("BULLA")]); + /** setup mock decimals and symbols for ADDRESS_2 and ADDRESS_4 */ + createMockedFunction(ADDRESS_2, "decimals", "decimals():(uint8)").returns([ethereum.Value.fromI32(18)]); + createMockedFunction(ADDRESS_2, "symbol", "symbol():(string)").returns([ethereum.Value.fromString("TKN2")]); + + createMockedFunction(ADDRESS_4, "decimals", "decimals():(uint8)").returns([ethereum.Value.fromI32(18)]); + createMockedFunction(ADDRESS_4, "symbol", "symbol():(string)").returns([ethereum.Value.fromString("TKN4")]); + /** setup BullaManager */ createMockedFunction(MOCK_MANAGER_ADDRESS, "description", "description():(bytes32)").returns([ethereum.Value.fromBytes(DESCRIPTION_BYTES)]);