diff --git a/packages/contracts/contracts/SafeSplitsDaoEscrowZap.sol b/packages/contracts/contracts/SafeSplitsDaoEscrowZap.sol index 7a76b167..c71b1c7c 100644 --- a/packages/contracts/contracts/SafeSplitsDaoEscrowZap.sol +++ b/packages/contracts/contracts/SafeSplitsDaoEscrowZap.sol @@ -51,37 +51,37 @@ contract SafeSplitsDaoEscrowZap is SafeSplitsEscrowZap { } } - if (daoSplit) { - // prepare arrays - address[] memory daoSplitRecipients = new address[](2); - uint32[] memory daoSplitPercentAllocations = new uint32[](2); - - // dao split recipients - address daoReceiver = spoilsManager.receiver(); - - // dao split amounts - uint32 daoSplitAmount = spoilsManager.getSpoils(); - uint32 projectSplitAmount = (100 * spoilsManager.SPLIT_PERCENTAGE_SCALE()) - (daoSplitAmount); - - // sort the addresses into the correct order - if (uint160(daoReceiver) < uint160(_daoZapData.zapData.projectTeamSplit)) { - daoSplitRecipients[0] = daoReceiver; - daoSplitRecipients[1] = _daoZapData.zapData.projectTeamSplit; - daoSplitPercentAllocations[0] = daoSplitAmount; - daoSplitPercentAllocations[1] = projectSplitAmount; - } else { - daoSplitRecipients[0] = _daoZapData.zapData.projectTeamSplit; - daoSplitRecipients[1] = daoReceiver; - daoSplitPercentAllocations[0] = projectSplitAmount; - daoSplitPercentAllocations[1] = daoSplitAmount; - } + if (!daoSplit) return _daoZapData; + + // prepare arrays + address[] memory daoSplitRecipients = new address[](2); + uint32[] memory daoSplitPercentAllocations = new uint32[](2); + + // dao split recipients + address daoReceiver = spoilsManager.receiver(); + + // dao split amounts + uint32 daoSplitAmount = spoilsManager.getSpoils(); + uint32 projectSplitAmount = (100 * spoilsManager.SPLIT_PERCENTAGE_SCALE()) - (daoSplitAmount); + + // sort the addresses into the correct order + if (uint160(daoReceiver) < uint160(_daoZapData.zapData.projectTeamSplit)) { + daoSplitRecipients[0] = daoReceiver; + daoSplitRecipients[1] = _daoZapData.zapData.projectTeamSplit; + daoSplitPercentAllocations[0] = daoSplitAmount; + daoSplitPercentAllocations[1] = projectSplitAmount; + } else { + daoSplitRecipients[0] = _daoZapData.zapData.projectTeamSplit; + daoSplitRecipients[1] = daoReceiver; + daoSplitPercentAllocations[0] = projectSplitAmount; + daoSplitPercentAllocations[1] = daoSplitAmount; + } - // (recipients array, percent allocations array, no distributor fee, safe address) - _daoZapData.daoSplit = - splitMain.createSplit(daoSplitRecipients, daoSplitPercentAllocations, distributorFee, dao); - if (_daoZapData.daoSplit == address(0)) { - revert DaoSplitCreationFailed(); - } + // (recipients array, percent allocations array, no distributor fee, safe address) + _daoZapData.daoSplit = + splitMain.createSplit(daoSplitRecipients, daoSplitPercentAllocations, distributorFee, dao); + if (_daoZapData.daoSplit == address(0)) { + revert DaoSplitCreationFailed(); } return _daoZapData; @@ -115,10 +115,12 @@ contract SafeSplitsDaoEscrowZap is SafeSplitsEscrowZap { daoSplit: address(0) }); + // optionally deploy safe if (daoZapData.zapData.safe == address(0)) { daoZapData.zapData = _deploySafe(_owners, _safeData, daoZapData.zapData); } + // create split(s) daoZapData = _createSplit(_owners, _percentAllocations, _splitsData, daoZapData); address[] memory escrowParams = _handleEscrowParams(daoZapData); diff --git a/packages/contracts/test/SafeSplitsDaoEscrowZap.js b/packages/contracts/test/SafeSplitsDaoEscrowZap.js index bf9e7e51..046f259b 100644 --- a/packages/contracts/test/SafeSplitsDaoEscrowZap.js +++ b/packages/contracts/test/SafeSplitsDaoEscrowZap.js @@ -1,7 +1,7 @@ const { expect } = require("chai"); const { ethers } = require("hardhat"); -const { currentTimestamp } = require("./utils"); +const { currentTimestamp, ZERO_ADDRESS } = require("./utils"); const { getZapData, @@ -52,6 +52,9 @@ describe("SafeSplitsDaoEscrowZap", function () { let escrow; let token; let receiver; + let encodedSafeData; + let encodedSplitData; + let encodedEscrowData; let i = 0; before(async function () { @@ -157,17 +160,16 @@ describe("SafeSplitsDaoEscrowZap", function () { beforeEach(async function () { i++; // increment to avoid nonce collisions in Create2 deployments - // create with zap - const encodedSafeData = ethers.utils.defaultAbiCoder.encode( + encodedSafeData = ethers.utils.defaultAbiCoder.encode( ["uint256", "uint256"], [ZAP_DATA.threshold, ZAP_DATA.saltNonce + i], ); - const encodedSplitData = ethers.utils.defaultAbiCoder.encode( + encodedSplitData = ethers.utils.defaultAbiCoder.encode( ["bool", "bool"], [ZAP_DATA.isProjectSplit, ZAP_DATA.isDaoSplit], ); - const encodedEscrowData = ethers.utils.defaultAbiCoder.encode( + encodedEscrowData = ethers.utils.defaultAbiCoder.encode( [ "address", "uint32", @@ -360,5 +362,149 @@ describe("SafeSplitsDaoEscrowZap", function () { ); }); + it("Should let the deployer skip the safe deploy", async function () { + const localEncodedEscrowData = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint32", + "address", + "address", + "uint256", + "uint256", + "bytes32", + ], + [ + ZAP_DATA.client, + ZAP_DATA.arbitration, + ZAP_DATA.resolver, + ZAP_DATA.token, + ZAP_DATA.escrowDeadline, + ZAP_DATA.saltNonce + 200 * i, + ZAP_DATA.details, + ], + ); + const SafeSplitsEscrowZapCreateReceipt = await zap.createSafeSplitEscrow( + ZAP_DATA.owners, + ZAP_DATA.percentAllocations, + ZAP_DATA.milestoneAmounts, + encodedSafeData, + deployer.address, + encodedSplitData, + localEncodedEscrowData, + ); + const zapCreateTx = await SafeSplitsEscrowZapCreateReceipt.wait(); + const zapCreatedEvent = zapCreateTx.events.find( + e => e.event === "SafeSplitsDaoEscrowCreated", + ); + const [safeAddress, teamSplitAddress, daoSplitAddress, escrowAddress] = + ethers.utils.defaultAbiCoder.decode( + ["address", "address", "address", "address"], + zapCreatedEvent.data, + ); + expect(safeAddress).to.equal(deployer.address); + }); + + it("Should let the deployer skip the team split", async function () { + const localEncodedSafeData = ethers.utils.defaultAbiCoder.encode( + ["uint256", "uint256"], + [ZAP_DATA.threshold, ZAP_DATA.saltNonce + 100 * i], + ); + const localEncodedSplitData = ethers.utils.defaultAbiCoder.encode( + ["bool", "bool"], + [false, true], + ); + const localEncodedEscrowData = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint32", + "address", + "address", + "uint256", + "uint256", + "bytes32", + ], + [ + ZAP_DATA.client, + ZAP_DATA.arbitration, + ZAP_DATA.resolver, + ZAP_DATA.token, + ZAP_DATA.escrowDeadline, + ZAP_DATA.saltNonce + 100 * i, + ZAP_DATA.details, + ], + ); + const SafeSplitsEscrowZapCreateReceipt = await zap.createSafeSplitEscrow( + ZAP_DATA.owners, + ZAP_DATA.percentAllocations, + ZAP_DATA.milestoneAmounts, + localEncodedSafeData, + ethers.constants.AddressZero, + localEncodedSplitData, + localEncodedEscrowData, + ); + const zapCreateTx = await SafeSplitsEscrowZapCreateReceipt.wait(); + const zapCreatedEvent = zapCreateTx.events.find( + e => e.event === "SafeSplitsDaoEscrowCreated", + ); + const [safeAddress, teamSplitAddress, daoSplitAddress, escrowAddress] = + ethers.utils.defaultAbiCoder.decode( + ["address", "address", "address", "address"], + zapCreatedEvent.data, + ); + + expect(teamSplitAddress).to.equal(safeAddress); + }); + + it("Should let the deployer skip the dao split", async function () { + const localEncodedSafeData = ethers.utils.defaultAbiCoder.encode( + ["uint256", "uint256"], + [ZAP_DATA.threshold, ZAP_DATA.saltNonce + 300 * i], + ); + const localEncodedSplitData = ethers.utils.defaultAbiCoder.encode( + ["bool", "bool"], + [true, false], + ); + const localEncodedEscrowData = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint32", + "address", + "address", + "uint256", + "uint256", + "bytes32", + ], + [ + ZAP_DATA.client, + ZAP_DATA.arbitration, + ZAP_DATA.resolver, + ZAP_DATA.token, + ZAP_DATA.escrowDeadline, + ZAP_DATA.saltNonce + 300 * i, + ZAP_DATA.details, + ], + ); + const SafeSplitsEscrowZapCreateReceipt = await zap.createSafeSplitEscrow( + ZAP_DATA.owners, + ZAP_DATA.percentAllocations, + ZAP_DATA.milestoneAmounts, + localEncodedSafeData, + ethers.constants.AddressZero, + localEncodedSplitData, + localEncodedEscrowData, + ); + const zapCreateTx = await SafeSplitsEscrowZapCreateReceipt.wait(); + const zapCreatedEvent = zapCreateTx.events.find( + e => e.event === "SafeSplitsDaoEscrowCreated", + ); + const [safeAddress, teamSplitAddress, daoSplitAddress, escrowAddress] = + ethers.utils.defaultAbiCoder.decode( + ["address", "address", "address", "address"], + zapCreatedEvent.data, + ); + + expect(daoSplitAddress).to.equal(ZERO_ADDRESS); + }); + // it should let the safe update the split - handle from Safe UI for now }); diff --git a/packages/dapp/constants/config.js b/packages/dapp/constants/config.js index b3d33ebe..edb64cef 100644 --- a/packages/dapp/constants/config.js +++ b/packages/dapp/constants/config.js @@ -21,7 +21,7 @@ export const CONFIG = { }, }, 100: { - SUBGRAPH: 'psparacino/v1-xdai-smart-invoices', + SUBGRAPH: 'scottrepreneur/smart-invoice-gnosis', WRAPPED_NATIVE_TOKEN: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d'.toLowerCase(), INVOICE_FACTORY: diff --git a/packages/subgraph/src/mappings/01/factory.ts b/packages/subgraph/src/mappings/01/factory.ts index 0a738170..e776bdb9 100644 --- a/packages/subgraph/src/mappings/01/factory.ts +++ b/packages/subgraph/src/mappings/01/factory.ts @@ -46,7 +46,7 @@ export function handleLogNewInvoice(event: LogNewInvoiceEvent): void { SmartInvoiceSplitEscrow01.create(event.params.invoice); } else if (invoice.invoiceType == 'updatable') { SmartInvoiceUpdatable01.create(event.params.invoice); - } else { + } else if (invoice.invoiceType == 'instant') { SmartInvoiceInstant01.create(event.params.invoice); } diff --git a/packages/subgraph/src/mappings/01/helpers/ipfs.ts b/packages/subgraph/src/mappings/01/helpers/ipfs.ts index 26fc43a4..576e0bf0 100644 --- a/packages/subgraph/src/mappings/01/helpers/ipfs.ts +++ b/packages/subgraph/src/mappings/01/helpers/ipfs.ts @@ -5,6 +5,7 @@ import { json, log, JSONValue, + JSONValueKind, } from '@graphprotocol/graph-ts'; import { Agreement } from '../../../types/schema'; @@ -54,6 +55,7 @@ export function handleIpfsDetails( invoiceObject: InvoiceObject, ): InvoiceObject { invoiceObject.details = details; + let hexHash = changetype(addQm(invoiceObject.details)); let base58Hash = hexHash.toBase58(); invoiceObject.ipfsHash = base58Hash.toString(); @@ -87,11 +89,19 @@ export function handleIpfsDetails( } } let startDate = data.get('startDate'); - if (startDate != null && !startDate.isNull()) { + if ( + startDate != null && + !startDate.isNull() && + startDate.kind == JSONValueKind.NUMBER + ) { invoiceObject.startDate = startDate.toBigInt(); } let endDate = data.get('endDate'); - if (endDate != null && !endDate.isNull()) { + if ( + endDate != null && + !endDate.isNull() && + endDate.kind == JSONValueKind.NUMBER + ) { invoiceObject.endDate = endDate.toBigInt(); }