From cca505b4a798a76b7c88935ab07280f786e684b8 Mon Sep 17 00:00:00 2001 From: jtourkos Date: Thu, 2 Oct 2025 15:10:14 +0300 Subject: [PATCH 1/6] feat: update metadata to support deadlines --- .../infrastructure/metadata/schemas/index.ts | 8 ++- .../metadata/schemas/nft-driver/v6.ts | 3 +- .../metadata/schemas/nft-driver/v7.ts | 55 +++++++++++++++++++ .../metadata/schemas/repo-driver/v6.ts | 46 ++++++++++++++++ 4 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 src/common/infrastructure/metadata/schemas/nft-driver/v7.ts create mode 100644 src/common/infrastructure/metadata/schemas/repo-driver/v6.ts diff --git a/src/common/infrastructure/metadata/schemas/index.ts b/src/common/infrastructure/metadata/schemas/index.ts index a83a2a3..2f3e09c 100644 --- a/src/common/infrastructure/metadata/schemas/index.ts +++ b/src/common/infrastructure/metadata/schemas/index.ts @@ -8,12 +8,15 @@ import {repoDriverAccountMetadataSchemaV2} from './repo-driver/v2'; import {repoDriverAccountMetadataSchemaV3} from './repo-driver/v3'; import {repoDriverAccountMetadataSchemaV4} from './repo-driver/v4'; import {nftDriverAccountMetadataSchemaV4} from './nft-driver/v4'; -import {nftDriverAccountMetadataSchemaV5} from './nft-driver/v5'; import {repoDriverAccountMetadataSchemaV5} from './repo-driver/v5'; -import {nftDriverAccountMetadataSchemaV6} from './nft-driver/v6'; +import {nftDriverAccountMetadataSchemaV5} from './nft-driver/v5'; import {subListMetadataSchemaV1} from './immutable-splits-driver/v1'; +import {nftDriverAccountMetadataSchemaV6} from './nft-driver/v6'; +import {nftDriverAccountMetadataSchemaV7} from './nft-driver/v7'; +import {repoDriverAccountMetadataSchemaV6} from './repo-driver/v6'; export const nftDriverAccountMetadataParser = createVersionedParser([ + nftDriverAccountMetadataSchemaV7.parse, nftDriverAccountMetadataSchemaV6.parse, nftDriverAccountMetadataSchemaV5.parse, nftDriverAccountMetadataSchemaV4.parse, @@ -27,6 +30,7 @@ export const addressDriverAccountMetadataParser = createVersionedParser([ ]); export const repoDriverAccountMetadataParser = createVersionedParser([ + repoDriverAccountMetadataSchemaV6.parse, repoDriverAccountMetadataSchemaV5.parse, repoDriverAccountMetadataSchemaV4.parse, repoDriverAccountMetadataSchemaV3.parse, diff --git a/src/common/infrastructure/metadata/schemas/nft-driver/v6.ts b/src/common/infrastructure/metadata/schemas/nft-driver/v6.ts index 4cd39da..2dcf570 100644 --- a/src/common/infrastructure/metadata/schemas/nft-driver/v6.ts +++ b/src/common/infrastructure/metadata/schemas/nft-driver/v6.ts @@ -15,6 +15,7 @@ const base = nftDriverAccountMetadataSchemaV5 projects: true, }) .extend({ + isDripList: z.undefined().optional(), projects: z.undefined().optional(), }); @@ -30,7 +31,7 @@ const ecosystemVariant = base.extend({ avatar: emojiAvatarSchema, }); -const dripListVariant = base.extend({ +export const dripListVariant = base.extend({ type: z.literal('dripList'), recipients: z.array( z.union([ diff --git a/src/common/infrastructure/metadata/schemas/nft-driver/v7.ts b/src/common/infrastructure/metadata/schemas/nft-driver/v7.ts new file mode 100644 index 0000000..a0d6090 --- /dev/null +++ b/src/common/infrastructure/metadata/schemas/nft-driver/v7.ts @@ -0,0 +1,55 @@ +import {z} from 'zod'; +import {nftDriverAccountMetadataSchemaV5} from './v5'; +import { + addressDriverSplitReceiverSchema, + repoDriverSplitReceiverSchema, +} from '../repo-driver/v2'; +import {subListSplitReceiverSchema} from '../immutable-splits-driver/v1'; +import {dripListSplitReceiverSchema} from './v2'; +import {repoSubAccountDriverSplitReceiverSchema} from '../common/repoSubAccountDriverSplitReceiverSchema'; +import {emojiAvatarSchema} from '../repo-driver/v4'; +import { + deadlineSplitReceiverSchema, + orcidSplitReceiverSchema, +} from '../repo-driver/v6'; + +const base = nftDriverAccountMetadataSchemaV5 + .omit({ + isDripList: true, + projects: true, + }) + .extend({ + allowExternalDonations: z.boolean().optional(), + }); + +const ecosystemVariant = base.extend({ + type: z.literal('ecosystem'), + recipients: z.array( + z.union([ + repoSubAccountDriverSplitReceiverSchema, + subListSplitReceiverSchema, + deadlineSplitReceiverSchema, // New in v7 + ]), + ), + color: z.string(), + avatar: emojiAvatarSchema, +}); + +const dripListVariant = base.extend({ + type: z.literal('dripList'), + recipients: z.array( + z.union([ + repoDriverSplitReceiverSchema, + subListSplitReceiverSchema, + addressDriverSplitReceiverSchema, + dripListSplitReceiverSchema, + deadlineSplitReceiverSchema, // New in v7 + orcidSplitReceiverSchema, // New in v7 + ]), + ), +}); + +export const nftDriverAccountMetadataSchemaV7 = z.discriminatedUnion('type', [ + ecosystemVariant, + dripListVariant, +]); diff --git a/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts b/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts new file mode 100644 index 0000000..62303c7 --- /dev/null +++ b/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts @@ -0,0 +1,46 @@ +import z from 'zod'; +import {gitHubSourceSchema} from '../common/sources'; +import { + addressDriverSplitReceiverSchema, + repoDriverSplitReceiverSchema, +} from './v2'; +import {dripListSplitReceiverSchema} from '../nft-driver/v2'; +import {repoDriverAccountMetadataSchemaV5} from './v5'; + +export const orcidSplitReceiverSchema = z.object({ + type: z.literal('orcid'), + weight: z.number(), + accountId: z.string(), + orcidId: z.string(), +}); + +export const deadlineSplitReceiverSchema = z.object({ + type: z.literal('deadline'), + weight: z.number(), + accountId: z.string(), + claimableProject: z.object({ + accountId: z.string(), + source: gitHubSourceSchema, + }), + recipientAccountId: z.string(), + refundAccountId: z.string(), + deadline: z.coerce.date(), +}); + +const repoDriverAccountSplitsSchemaV6 = z.object({ + maintainers: z.array(addressDriverSplitReceiverSchema), + dependencies: z.array( + z.union([ + dripListSplitReceiverSchema, + repoDriverSplitReceiverSchema, + addressDriverSplitReceiverSchema, + deadlineSplitReceiverSchema, // New in v6 + orcidSplitReceiverSchema, // New in v6 + ]), + ), +}); + +export const repoDriverAccountMetadataSchemaV6 = + repoDriverAccountMetadataSchemaV5.extend({ + splits: repoDriverAccountSplitsSchemaV6, + }); From ab2ec7ac1bce8b6dc77163a6b8b52717b6e87ad9 Mon Sep 17 00:00:00 2001 From: jtourkos Date: Thu, 2 Oct 2025 15:10:44 +0300 Subject: [PATCH 2/6] feat: add deadline columns --- src/common/domain/entities.ts/Ecosystem.ts | 6 +++++ .../1759406128330-AddDeadlineColumns.ts | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/common/infrastructure/migrations/1759406128330-AddDeadlineColumns.ts diff --git a/src/common/domain/entities.ts/Ecosystem.ts b/src/common/domain/entities.ts/Ecosystem.ts index 2541ef6..a1ff873 100644 --- a/src/common/domain/entities.ts/Ecosystem.ts +++ b/src/common/domain/entities.ts/Ecosystem.ts @@ -70,4 +70,10 @@ export class Ecosystem { @Column({type: 'varchar', length: 9}) color!: string; + + @Column({type: 'timestamp', nullable: true}) + deadline!: Date | null; + + @Column({type: 'varchar', length: 200, nullable: true}) + refundAccountId!: AccountId | null; } diff --git a/src/common/infrastructure/migrations/1759406128330-AddDeadlineColumns.ts b/src/common/infrastructure/migrations/1759406128330-AddDeadlineColumns.ts new file mode 100644 index 0000000..63de030 --- /dev/null +++ b/src/common/infrastructure/migrations/1759406128330-AddDeadlineColumns.ts @@ -0,0 +1,22 @@ +import {MigrationInterface, QueryRunner} from 'typeorm'; + +export class AddDeadlineColumns1759406128330 implements MigrationInterface { + name = 'AddDeadlineColumns1759406128330'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'ALTER TABLE "Ecosystems" ADD "deadline" TIMESTAMP', + ); + + await queryRunner.query( + 'ALTER TABLE "Ecosystems" ADD "refundAccountId" character varying(200)', + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE "Ecosystems" DROP COLUMN "deadline"'); + await queryRunner.query( + 'ALTER TABLE "Ecosystems" DROP COLUMN "refundAccountId"', + ); + } +} From 3029d3b841e0cbc12ea0a59c19d2019222d58df4 Mon Sep 17 00:00:00 2001 From: jtourkos Date: Thu, 2 Oct 2025 15:18:55 +0300 Subject: [PATCH 3/6] feat: add repo deadline driver client --- .../contracts/contractsConfig.ts | 12 +- .../repoDeadlineDriver/repoDeadlineDriver.ts | 56 +++ .../repoDeadlineDriverAbi.ts | 374 ++++++++++++++++++ 3 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriver.ts create mode 100644 src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriverAbi.ts diff --git a/src/common/infrastructure/contracts/contractsConfig.ts b/src/common/infrastructure/contracts/contractsConfig.ts index 0d7d326..687a23b 100644 --- a/src/common/infrastructure/contracts/contractsConfig.ts +++ b/src/common/infrastructure/contracts/contractsConfig.ts @@ -6,9 +6,10 @@ type ContractsConfig = { CALLER: OxString; REPO_DRIVER: OxString; REPO_SUB_ACCOUNT_DRIVER: OxString | undefined; // TODO: remove this when repo sub account driver is deployed on all chains. + REPO_DEADLINE_DRIVER: OxString | undefined; NFT_DRIVER: OxString; IMMUTABLE_SPLITS: OxString; - NATIVE_TOKEN_UNWRAPPER: OxString | undefined; // TODO: remove this when repo sub account driver is deployed on all chains. + NATIVE_TOKEN_UNWRAPPER: OxString | undefined; // TODO: remove this when native token unwrapper is deployed on all chains. }; export const contractsConfigByChainId: Record = { @@ -22,6 +23,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x1212975c0642B07F696080ec1916998441c2b774', NATIVE_TOKEN_UNWRAPPER: '0xa4d564894eb4b318e06adbc284295b6597a22019', REPO_SUB_ACCOUNT_DRIVER: '0xc219395880fa72e3ad9180b8878e0d39d144130b', + REPO_DEADLINE_DRIVER: undefined, }, // Polygon Amoy 80002: { @@ -33,6 +35,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x65A48270e51A7aa901fD8fc42ab9cDddb50aff05', NATIVE_TOKEN_UNWRAPPER: undefined, REPO_SUB_ACCOUNT_DRIVER: undefined, + REPO_DEADLINE_DRIVER: undefined, }, // Optimism Sepolia 11155420: { @@ -44,6 +47,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x1346abCa70e010fe8B01C2A7424dE4E83921018D', NATIVE_TOKEN_UNWRAPPER: '0x0288C45cA3ae6d226f3fCC45c340A13914f0FaF2', REPO_SUB_ACCOUNT_DRIVER: '0xe077e0D50fB60b900467F4a44DF7b49deB41097d', + REPO_DEADLINE_DRIVER: undefined, }, // Optimism Mainnet 10: { @@ -55,6 +59,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', NATIVE_TOKEN_UNWRAPPER: '0x64e0d60C70e9778C2E649FfBc90259C86a6Bf396', REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', + REPO_DEADLINE_DRIVER: undefined, }, // Sepolia 11155111: { @@ -66,6 +71,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0xC3C1955bb50AdA4dC8a55aBC6d4d2a39242685c1', NATIVE_TOKEN_UNWRAPPER: '0xf470601db8f7eebac6612a16bf15daed0f414f6a', REPO_SUB_ACCOUNT_DRIVER: '0x317400fd9dfdad78d53a34455d89beb8f03f90ee', + REPO_DEADLINE_DRIVER: undefined, }, // Local testnet 31337: { @@ -77,6 +83,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x33a946e876C3bFb08636099238Db35a81dEf4b1E', NATIVE_TOKEN_UNWRAPPER: '0xC203cF8A0B0594fD404fe97725802bE59eF64194', REPO_SUB_ACCOUNT_DRIVER: '0xB8743C2bB8DF7399273aa7EE4cE8d4109Bec327F', + REPO_DEADLINE_DRIVER: undefined, }, // Base Sepolia 84532: { @@ -88,6 +95,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x65A48270e51A7aa901fD8fc42ab9cDddb50aff05', NATIVE_TOKEN_UNWRAPPER: undefined, REPO_SUB_ACCOUNT_DRIVER: undefined, + REPO_DEADLINE_DRIVER: undefined, }, // Filecoin 314: { @@ -99,6 +107,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', NATIVE_TOKEN_UNWRAPPER: '0x64e0d60C70e9778C2E649FfBc90259C86a6Bf396', REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', + REPO_DEADLINE_DRIVER: undefined, }, // Metis 1088: { @@ -110,5 +119,6 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', NATIVE_TOKEN_UNWRAPPER: undefined, REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', + REPO_DEADLINE_DRIVER: undefined, }, }; diff --git a/src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriver.ts b/src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriver.ts new file mode 100644 index 0000000..58b88ce --- /dev/null +++ b/src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriver.ts @@ -0,0 +1,56 @@ +import type { + AbiFunction, + AbiParametersToPrimitiveTypes, + ExtractAbiFunction, + ExtractAbiFunctionNames, +} from 'abitype'; +import { + repoDeadlineDriverAbi, + RepoDeadlineDriverAbi, +} from './repoDeadlineDriverAbi'; +import {unwrapEthersResult, UnwrappedEthersResult} from '../unwrapEthersResult'; +import {Contract} from 'ethers'; +import getWallet from '../getWallet'; +import {ChainId} from '../../../domain/types'; +import {contractsConfigByChainId} from '../contractsConfig'; + +export async function executeRepoDeadlineDriverReadMethod< + functionName extends ExtractAbiFunctionNames< + RepoDeadlineDriverAbi, + 'pure' | 'view' + >, + abiFunction extends AbiFunction = ExtractAbiFunction< + RepoDeadlineDriverAbi, + functionName + >, +>(config: { + functionName: + | functionName + | ExtractAbiFunctionNames; + args: AbiParametersToPrimitiveTypes; + chainId: ChainId; +}): Promise< + UnwrappedEthersResult< + AbiParametersToPrimitiveTypes + > +> { + const {functionName: func, args, chainId} = config; + + const wallet = getWallet(chainId); + const contractAddress = + contractsConfigByChainId[chainId].REPO_DEADLINE_DRIVER; + + if (!contractAddress) { + throw new Error( + `Contract address for REPO_DEADLINE_DRIVER not found for chainId: ${chainId}`, + ); + } + + const repoDeadlineDriver = new Contract( + contractAddress, + repoDeadlineDriverAbi, + wallet, + ); + + return unwrapEthersResult(await repoDeadlineDriver[func](...args)); +} diff --git a/src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriverAbi.ts b/src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriverAbi.ts new file mode 100644 index 0000000..33a3dbc --- /dev/null +++ b/src/common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriverAbi.ts @@ -0,0 +1,374 @@ +export const repoDeadlineDriverAbi = [ + { + inputs: [ + { + internalType: 'contract RepoDriver', + name: 'repoDriver_', + type: 'address', + }, + {internalType: 'uint32', name: 'driverId_', type: 'uint32'}, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'accountId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'repoAccountId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'recipientAccountId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'uint256', + name: 'refundAccountId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint32', + name: 'deadline', + type: 'uint32', + }, + ], + name: 'AccountSeen', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'previousAdmin', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'AdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'beacon', + type: 'address', + }, + ], + name: 'BeaconUpgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'currentAdmin', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'NewAdminProposed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'admin', + type: 'address', + }, + ], + name: 'PauserGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'admin', + type: 'address', + }, + ], + name: 'PauserRevoked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + ], + name: 'Unpaused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'acceptAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'admin', + outputs: [{internalType: 'address', name: '', type: 'address'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'allPausers', + outputs: [ + { + internalType: 'address[]', + name: 'pausersList', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + {internalType: 'uint256', name: 'repoAccountId', type: 'uint256'}, + { + internalType: 'uint256', + name: 'recipientAccountId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'refundAccountId', + type: 'uint256', + }, + {internalType: 'uint32', name: 'deadline', type: 'uint32'}, + ], + name: 'calcAccountId', + outputs: [{internalType: 'uint256', name: 'accountId', type: 'uint256'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + {internalType: 'uint256', name: 'repoAccountId', type: 'uint256'}, + { + internalType: 'uint256', + name: 'recipientAccountId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'refundAccountId', + type: 'uint256', + }, + {internalType: 'uint32', name: 'deadline', type: 'uint32'}, + {internalType: 'contract IERC20', name: 'erc20', type: 'address'}, + ], + name: 'collectAndGive', + outputs: [{internalType: 'uint128', name: 'amt', type: 'uint128'}], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'drips', + outputs: [{internalType: 'contract Drips', name: '', type: 'address'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'driverId', + outputs: [{internalType: 'uint32', name: '', type: 'uint32'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{internalType: 'address', name: 'pauser', type: 'address'}], + name: 'grantPauser', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'implementation', + outputs: [{internalType: 'address', name: '', type: 'address'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isPaused', + outputs: [{internalType: 'bool', name: '', type: 'bool'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{internalType: 'address', name: 'pauser', type: 'address'}], + name: 'isPauser', + outputs: [{internalType: 'bool', name: 'isAddrPauser', type: 'bool'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'pause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{internalType: 'address', name: 'newAdmin', type: 'address'}], + name: 'proposeNewAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'proposedAdmin', + outputs: [{internalType: 'address', name: '', type: 'address'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [{internalType: 'bytes32', name: '', type: 'bytes32'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'repoDriver', + outputs: [{internalType: 'contract RepoDriver', name: '', type: 'address'}], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{internalType: 'address', name: 'pauser', type: 'address'}], + name: 'revokePauser', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'unpause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'upgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + {internalType: 'bytes', name: 'data', type: 'bytes'}, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, +] as const; + +export type RepoDeadlineDriverAbi = typeof repoDeadlineDriverAbi; From fbb11123b4ff7835ea30cd398ad0ce5ce18441f0 Mon Sep 17 00:00:00 2001 From: jtourkos Date: Thu, 2 Oct 2025 15:19:19 +0300 Subject: [PATCH 4/6] feat: leftover from metadata updates --- .../infrastructure/metadata/schemas/common/sources.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/common/infrastructure/metadata/schemas/common/sources.ts b/src/common/infrastructure/metadata/schemas/common/sources.ts index 847f774..60e1f6b 100644 --- a/src/common/infrastructure/metadata/schemas/common/sources.ts +++ b/src/common/infrastructure/metadata/schemas/common/sources.ts @@ -1,11 +1,14 @@ import {z} from 'zod'; -const gitHubSourceSchema = z.object({ +export const gitHubSourceSchema = z.object({ forge: z.literal('github'), repoName: z.string(), ownerName: z.string(), url: z.string(), }); -// This will be a union type when we add support for other forges. -export const sourceSchema = gitHubSourceSchema; +const orcidSourceSchema = z.object({ + forge: z.literal('orcid'), +}); + +export const sourceSchema = z.union([gitHubSourceSchema, orcidSourceSchema]); From 453afb871a2f4bc05cb4621321840137d664b415 Mon Sep 17 00:00:00 2001 From: jtourkos Date: Tue, 7 Oct 2025 12:34:25 +0300 Subject: [PATCH 5/6] feat: support deadlines --- .../contracts/contractsConfig.ts | 12 +-- .../schemas/immutable-splits-driver/v2.ts | 19 ++++ .../infrastructure/metadata/schemas/index.ts | 2 + .../metadata/schemas/repo-driver/v6.ts | 9 +- .../api/createEcosystemDtos.ts | 60 ++++++++++-- .../database/ecosystemRepository.ts | 15 ++- .../convertToEcosystemMainAccount.ts | 91 ++++++++++++++++--- .../application/deployEcosystemHandler.ts | 5 +- .../deployEcosystem/application/types.ts | 5 +- .../blockchain/createEcosystem.ts | 13 ++- .../infrastructure/ipfs/metadata.ts | 9 +- 11 files changed, 194 insertions(+), 46 deletions(-) create mode 100644 src/common/infrastructure/metadata/schemas/immutable-splits-driver/v2.ts diff --git a/src/common/infrastructure/contracts/contractsConfig.ts b/src/common/infrastructure/contracts/contractsConfig.ts index 687a23b..ba35b6f 100644 --- a/src/common/infrastructure/contracts/contractsConfig.ts +++ b/src/common/infrastructure/contracts/contractsConfig.ts @@ -47,7 +47,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x1346abCa70e010fe8B01C2A7424dE4E83921018D', NATIVE_TOKEN_UNWRAPPER: '0x0288C45cA3ae6d226f3fCC45c340A13914f0FaF2', REPO_SUB_ACCOUNT_DRIVER: '0xe077e0D50fB60b900467F4a44DF7b49deB41097d', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0xE57A3111414E0FaB39cc6e8fDe957b1f6471cd49', }, // Optimism Mainnet 10: { @@ -59,7 +59,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', NATIVE_TOKEN_UNWRAPPER: '0x64e0d60C70e9778C2E649FfBc90259C86a6Bf396', REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0x0386b66e2b0106ff27ef26e84102ca78a5c0edef', }, // Sepolia 11155111: { @@ -71,7 +71,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0xC3C1955bb50AdA4dC8a55aBC6d4d2a39242685c1', NATIVE_TOKEN_UNWRAPPER: '0xf470601db8f7eebac6612a16bf15daed0f414f6a', REPO_SUB_ACCOUNT_DRIVER: '0x317400fd9dfdad78d53a34455d89beb8f03f90ee', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0x4e576318213e3c9b436d0758a021a485c5d8b929', }, // Local testnet 31337: { @@ -83,7 +83,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x33a946e876C3bFb08636099238Db35a81dEf4b1E', NATIVE_TOKEN_UNWRAPPER: '0xC203cF8A0B0594fD404fe97725802bE59eF64194', REPO_SUB_ACCOUNT_DRIVER: '0xB8743C2bB8DF7399273aa7EE4cE8d4109Bec327F', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0xFD9Aa049A4f3dC1a2CD3355Ce52A943418Fa54e3', }, // Base Sepolia 84532: { @@ -107,7 +107,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', NATIVE_TOKEN_UNWRAPPER: '0x64e0d60C70e9778C2E649FfBc90259C86a6Bf396', REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0x0386b66e2b0106ff27ef26e84102ca78a5c0edef', }, // Metis 1088: { @@ -119,6 +119,6 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', NATIVE_TOKEN_UNWRAPPER: undefined, REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0x0386b66e2b0106ff27ef26e84102ca78a5c0edef', }, }; diff --git a/src/common/infrastructure/metadata/schemas/immutable-splits-driver/v2.ts b/src/common/infrastructure/metadata/schemas/immutable-splits-driver/v2.ts new file mode 100644 index 0000000..2f5a0a4 --- /dev/null +++ b/src/common/infrastructure/metadata/schemas/immutable-splits-driver/v2.ts @@ -0,0 +1,19 @@ +import z from 'zod'; +import {addressDriverSplitReceiverSchema} from '../repo-driver/v2'; +import {dripListSplitReceiverSchema} from '../nft-driver/v2'; +import {repoSubAccountDriverSplitReceiverSchema} from '../common/repoSubAccountDriverSplitReceiverSchema'; +import {deadlineSplitReceiverSchema} from '../repo-driver/v6'; +import {subListSplitReceiverSchema, subListMetadataSchemaV1} from './v1'; + +export const subListMetadataSchemaV2 = subListMetadataSchemaV1.extend({ + recipients: z.array( + z.union([ + addressDriverSplitReceiverSchema, + dripListSplitReceiverSchema, + repoSubAccountDriverSplitReceiverSchema, + subListSplitReceiverSchema, + deadlineSplitReceiverSchema, // New in v2 + ]), + ), + isVisible: z.boolean().optional(), +}); diff --git a/src/common/infrastructure/metadata/schemas/index.ts b/src/common/infrastructure/metadata/schemas/index.ts index 2f3e09c..9723613 100644 --- a/src/common/infrastructure/metadata/schemas/index.ts +++ b/src/common/infrastructure/metadata/schemas/index.ts @@ -11,6 +11,7 @@ import {nftDriverAccountMetadataSchemaV4} from './nft-driver/v4'; import {repoDriverAccountMetadataSchemaV5} from './repo-driver/v5'; import {nftDriverAccountMetadataSchemaV5} from './nft-driver/v5'; import {subListMetadataSchemaV1} from './immutable-splits-driver/v1'; +import {subListMetadataSchemaV2} from './immutable-splits-driver/v2'; import {nftDriverAccountMetadataSchemaV6} from './nft-driver/v6'; import {nftDriverAccountMetadataSchemaV7} from './nft-driver/v7'; import {repoDriverAccountMetadataSchemaV6} from './repo-driver/v6'; @@ -39,5 +40,6 @@ export const repoDriverAccountMetadataParser = createVersionedParser([ ]); export const immutableSplitsDriverMetadataParser = createVersionedParser([ + subListMetadataSchemaV2.parse, subListMetadataSchemaV1.parse, ]); diff --git a/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts b/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts index 62303c7..99ed9ef 100644 --- a/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts +++ b/src/common/infrastructure/metadata/schemas/repo-driver/v6.ts @@ -24,7 +24,14 @@ export const deadlineSplitReceiverSchema = z.object({ }), recipientAccountId: z.string(), refundAccountId: z.string(), - deadline: z.coerce.date(), + deadline: z.coerce + .date() + .refine(date => !isNaN(date.getTime()), 'Invalid date') + .refine(date => date > new Date(), 'Deadline must be in the future') + .refine( + date => date < new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), + 'Deadline cannot be more than 1 year in the future', + ), }); const repoDriverAccountSplitsSchemaV6 = z.object({ diff --git a/src/features/createEcosystem/api/createEcosystemDtos.ts b/src/features/createEcosystem/api/createEcosystemDtos.ts index c292e6b..84b288c 100644 --- a/src/features/createEcosystem/api/createEcosystemDtos.ts +++ b/src/features/createEcosystem/api/createEcosystemDtos.ts @@ -47,16 +47,56 @@ const emojiAvatarSchema = z.object({ emoji: z.string(), }); -export const newEcosystemRequestSchema = z.object({ - graph: graphSchema, - metadata: metadataSchema, - ownerAddress: addressSchema, - name: z.string().min(1).max(100), - description: z.string().min(1).max(1000).optional(), - chainId: z.enum(Object.values(SUPPORTED_CHAIN_IDS) as [ChainId]), - avatar: emojiAvatarSchema, - color: hexColorSchema, -}); +const deadlineSchema = z.coerce + .date() + .refine(date => !isNaN(date.getTime()), 'Invalid date') + .refine(date => date > new Date(), 'Deadline must be in the future') + .refine( + date => date < new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), + 'Deadline cannot be more than 1 year in the future', + ); + +export const newEcosystemRequestSchema = z + .object({ + graph: graphSchema, + metadata: metadataSchema, + ownerAddress: addressSchema, + name: z.string().min(1).max(100), + description: z.string().min(1).max(1000).optional(), + chainId: z.enum(Object.values(SUPPORTED_CHAIN_IDS) as [ChainId]), + avatar: emojiAvatarSchema, + color: hexColorSchema, + deadline: deadlineSchema.optional(), + refundAccountId: z.string().optional(), + }) + .superRefine( + ( + data: {deadline?: Date; refundAccountId?: string}, + ctx: z.RefinementCtx, + ): void => { + const hasDeadline: boolean = data.deadline !== undefined; + const hasRefundAccountId: boolean = data.refundAccountId !== undefined; + + if (hasDeadline === hasRefundAccountId) { + return; + } + + const sharedMessage = + 'Both deadline and refundAccountId must be provided together, or neither.'; + + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: sharedMessage, + path: ['deadline'], + }); + + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: sharedMessage, + path: ['refundAccountId'], + }); + }, + ); export type NodeDto = z.infer; export type EdgeDto = z.infer; diff --git a/src/features/createEcosystem/infrastructure/database/ecosystemRepository.ts b/src/features/createEcosystem/infrastructure/database/ecosystemRepository.ts index 9813d0f..64819be 100644 --- a/src/features/createEcosystem/infrastructure/database/ecosystemRepository.ts +++ b/src/features/createEcosystem/infrastructure/database/ecosystemRepository.ts @@ -4,16 +4,17 @@ import unreachable from '../../../../common/application/unreachable'; import {Ecosystem} from '../../../../common/domain/entities.ts/Ecosystem'; import {Edge} from '../../../../common/domain/entities.ts/Edge'; import {Node} from '../../../../common/domain/entities.ts/Node'; -import {OxString, ProjectName} from '../../../../common/domain/types'; +import { + AccountId, + OxString, + ProjectName, +} from '../../../../common/domain/types'; import {NewEcosystemRequestDto} from '../../api/createEcosystemDtos'; import {dataSource} from '../../../../common/infrastructure/datasource'; import {NotFoundError} from '../../../../common/application/HttpError'; import {assertIsProjectName} from '../../../../common/application/assertions'; import {logger} from '../../../../common/infrastructure/logger'; -import { - ProjectVerificationResult, - SuccessfulProjectVerificationResult, -} from '../redis/createRedisOptions'; +import {SuccessfulProjectVerificationResult} from '../redis/createRedisOptions'; export async function getEcosystemById( ecosystemId: UUID, @@ -247,6 +248,8 @@ export const saveEcosystemIfNotExist = async ( description, avatar, color, + deadline, + refundAccountId, } = newEcosystem; const entity = repository.create({ name, @@ -259,6 +262,8 @@ export const saveEcosystemIfNotExist = async ( rawGraph: graph, metadata, state: 'processing_graph', + deadline: deadline ?? null, + refundAccountId: (refundAccountId as AccountId) ?? null, }); await repository.save(entity); diff --git a/src/features/deployEcosystem/application/convertToEcosystemMainAccount.ts b/src/features/deployEcosystem/application/convertToEcosystemMainAccount.ts index 7246d6e..5721cb9 100644 --- a/src/features/deployEcosystem/application/convertToEcosystemMainAccount.ts +++ b/src/features/deployEcosystem/application/convertToEcosystemMainAccount.ts @@ -1,19 +1,24 @@ import {Node} from '../../../common/domain/entities.ts/Node'; -import {ProjectReceiver} from './types'; +import {ProjectReceiver, DeadlineReceiver} from './types'; import {logger} from '../../../common/infrastructure/logger'; import unreachable from '../../../common/application/unreachable'; import {AccountId, ChainId, OxString} from '../../../common/domain/types'; import calculateRandomSalt from '../infrastructure/blockchain/calculateRandomSalt'; import {executeNftDriverReadMethod} from '../../../common/infrastructure/contracts/nftDriver/nftDriver'; import getWallet from '../../../common/infrastructure/contracts/getWallet'; +import {executeRepoDeadlineDriverReadMethod} from '../../../common/infrastructure/contracts/repoDeadlineDriver/repoDeadlineDriver'; +import {executeRepoSubAccountDriverReadMethod} from '../../../common/infrastructure/contracts/repoSubAccountDriver/repoSubAccountDriver'; +import {toBigInt} from 'ethers'; + +type Receiver = ProjectReceiver | DeadlineReceiver; // In the future, we may allow `SubListReceiver` types as well. type EcosystemMainAccount = { - projectReceivers: ProjectReceiver[]; - subListReceivers: ProjectReceiver[][]; // In the future, we may allow `SubListReceiver` types as well. + projectReceivers: Receiver[]; + subListReceivers: Receiver[][]; }; type NormalizedSubList = { - receivers: ProjectReceiver[]; + receivers: Receiver[]; normalizedWeight: number; }; @@ -21,7 +26,7 @@ export type NormalizedEcosystemMainAccount = { salt: bigint; accountId: AccountId; subLists: NormalizedSubList[]; - projectReceivers: ProjectReceiver[]; + projectReceivers: (ProjectReceiver | DeadlineReceiver)[]; }; const MAX_SPLITS_RECEIVERS = 2; // Hardcoded in Drips contracts. @@ -106,6 +111,8 @@ function normalizeWeights( export default async function convertToEcosystemMainAccount( nodes: Node[], chainId: ChainId, + deadline: Date | null, + refundAccountId: AccountId | null, ): Promise { if (nodes.length > MAX_NUMBER_OF_NODES) { throw new Error( @@ -132,7 +139,9 @@ export default async function convertToEcosystemMainAccount( return normalizeEcosystemMainAccount( { projectReceivers: await Promise.all( - allNodesExceptRoot.map(node => mapToProjectReceiver(node)), + allNodesExceptRoot.map(node => + mapToReceiver(node, chainId, deadline, refundAccountId), + ), ), subListReceivers: [], }, @@ -155,7 +164,7 @@ export default async function convertToEcosystemMainAccount( const rawReceiversCount = MAX_SPLITS_RECEIVERS - subListsNeeded; const subListsCount = totalIDs - rawReceiversCount; - const subListReceivers: ProjectReceiver[][] = []; + const subListReceivers: Receiver[][] = []; let processedAccounts = 0; while (processedAccounts < subListsCount) { const start = processedAccounts; @@ -166,7 +175,11 @@ export default async function convertToEcosystemMainAccount( ); if (subListSlice.length > 0) { subListReceivers.push( - await Promise.all(subListSlice.map(node => mapToProjectReceiver(node))), + await Promise.all( + subListSlice.map(node => + mapToReceiver(node, chainId, deadline, refundAccountId), + ), + ), ); } processedAccounts += subListSlice.length; @@ -177,7 +190,7 @@ export default async function convertToEcosystemMainAccount( projectReceivers: await Promise.all( allNodesExceptRoot .slice(0, rawReceiversCount) - .map(node => mapToProjectReceiver(node)), + .map(node => mapToReceiver(node, chainId, deadline, refundAccountId)), ), subListReceivers, }, @@ -185,14 +198,17 @@ export default async function convertToEcosystemMainAccount( ); } -async function mapToProjectReceiver( +async function mapToReceiver( node: Node & {projectAccountId: string; url: string}, -): Promise { + chainId: ChainId, + deadline: Date | null, + refundAccountId: AccountId | null, +): Promise { const [ownerName, repoName] = node.projectName.includes('/') ? node.projectName.split('/') : unreachable('Invalid project name format.'); - return { + const projectReceiver: ProjectReceiver = { accountId: node.projectAccountId, weight: node.absoluteWeight, type: 'repoSubAccountDriver', @@ -203,6 +219,57 @@ async function mapToProjectReceiver( repoName, }, }; + + // If no deadline is configured, return the project receiver directly. + if (!deadline || !refundAccountId) { + return projectReceiver; + } + + // Calculate the RepoDriver accountId from the RepoSubAccountDriver accountId. + const repoAccountId = ( + await executeRepoSubAccountDriverReadMethod({ + functionName: 'calcAccountId', + args: [toBigInt(projectReceiver.accountId)], + chainId, + }) + ).toString() as AccountId; + + // Convert deadline to Unix timestamp (seconds). + const deadlineTimestamp = toBigInt(Math.floor(deadline.getTime() / 1000)); + + // Calculate the DeadlineDriver account ID. + const deadlineAccountId = ( + await executeRepoDeadlineDriverReadMethod({ + functionName: 'calcAccountId', + args: [ + toBigInt(repoAccountId), + toBigInt(projectReceiver.accountId), + toBigInt(refundAccountId), + Number(deadlineTimestamp), + ], + chainId, + }) + ).toString() as AccountId; + + // Ensure the source is GitHub for deadline receivers. + if (projectReceiver.source.forge !== 'github') { + unreachable('Deadline receivers are only supported for GitHub projects.'); + } + + const deadlineReceiver: DeadlineReceiver = { + type: 'deadline', + weight: projectReceiver.weight, + accountId: deadlineAccountId, + claimableProject: { + accountId: repoAccountId, + source: projectReceiver.source, + }, + recipientAccountId: projectReceiver.accountId, + refundAccountId, + deadline, + }; + + return deadlineReceiver; } /** diff --git a/src/features/deployEcosystem/application/deployEcosystemHandler.ts b/src/features/deployEcosystem/application/deployEcosystemHandler.ts index 9af35ca..d97342c 100644 --- a/src/features/deployEcosystem/application/deployEcosystemHandler.ts +++ b/src/features/deployEcosystem/application/deployEcosystemHandler.ts @@ -18,7 +18,8 @@ export const handleDeployEcosystem = async ({ }: DeployEcosystemRequestDto) => { assertIsUUID(id); - const {chainId, ownerAddress, state} = await getEcosystemById(id); + const {chainId, ownerAddress, state, deadline, refundAccountId} = + await getEcosystemById(id); if (state !== 'pending_deployment') { throw new BadRequestError( @@ -30,6 +31,8 @@ export const handleDeployEcosystem = async ({ const ecosystemMainAccount = await convertToEcosystemMainAccount( nodes, chainId, + deadline, + refundAccountId, ); await transitionEcosystemState(id, 'DEPLOYMENT_STARTED'); diff --git a/src/features/deployEcosystem/application/types.ts b/src/features/deployEcosystem/application/types.ts index 96dd484..ca79d89 100644 --- a/src/features/deployEcosystem/application/types.ts +++ b/src/features/deployEcosystem/application/types.ts @@ -3,6 +3,7 @@ import {dripListSplitReceiverSchema} from '../../../common/infrastructure/metada import {addressDriverSplitReceiverSchema} from '../../../common/infrastructure/metadata/schemas/repo-driver/v2'; import {subListSplitReceiverSchema} from '../../../common/infrastructure/metadata/schemas/immutable-splits-driver/v1'; import {repoSubAccountDriverSplitReceiverSchema} from '../../../common/infrastructure/metadata/schemas/common/repoSubAccountDriverSplitReceiverSchema'; +import {deadlineSplitReceiverSchema} from '../../../common/infrastructure/metadata/schemas/repo-driver/v6'; export type TransactionExecutionStrategy = { executeTx: (context: TContext) => Promise; @@ -14,8 +15,10 @@ export type ProjectReceiver = z.infer< export type SubListReceiver = z.infer; export type DripListReceiver = z.infer; export type AddressReceiver = z.infer; +export type DeadlineReceiver = z.infer; export type Receiver = | ProjectReceiver | SubListReceiver | DripListReceiver - | AddressReceiver; + | AddressReceiver + | DeadlineReceiver; diff --git a/src/features/deployEcosystem/infrastructure/blockchain/createEcosystem.ts b/src/features/deployEcosystem/infrastructure/blockchain/createEcosystem.ts index 1d40a51..1ad42d8 100644 --- a/src/features/deployEcosystem/infrastructure/blockchain/createEcosystem.ts +++ b/src/features/deployEcosystem/infrastructure/blockchain/createEcosystem.ts @@ -8,7 +8,11 @@ import {populateEcosystemMainAccountCreationTxs} from './populateTransactions'; import {pinEcosystemMetadata} from '../ipfs/metadata'; import unreachable from '../../../../common/application/unreachable'; import {NormalizedEcosystemMainAccount} from '../../application/convertToEcosystemMainAccount'; -import {ProjectReceiver, SubListReceiver} from '../../application/types'; +import { + ProjectReceiver, + SubListReceiver, + DeadlineReceiver, +} from '../../application/types'; type Params = { chainId: ChainId; @@ -37,10 +41,9 @@ export default async function createEcosystem({ ); } - const receivers = [...ecosystemMainAccount.projectReceivers] as ( - | ProjectReceiver - | SubListReceiver - )[]; + const receivers: (ProjectReceiver | DeadlineReceiver | SubListReceiver)[] = [ + ...ecosystemMainAccount.projectReceivers, + ]; if (successfulSubListCreationResults.length) { receivers.push( ...successfulSubListCreationResults.flatMap( diff --git a/src/features/deployEcosystem/infrastructure/ipfs/metadata.ts b/src/features/deployEcosystem/infrastructure/ipfs/metadata.ts index adec531..49cd272 100644 --- a/src/features/deployEcosystem/infrastructure/ipfs/metadata.ts +++ b/src/features/deployEcosystem/infrastructure/ipfs/metadata.ts @@ -11,6 +11,7 @@ import {logger} from '../../../../common/infrastructure/logger'; import {encodeBytes32String, hexlify, toUtf8Bytes} from 'ethers'; import { ProjectReceiver, + DeadlineReceiver, Receiver, SubListReceiver, } from '../../application/types'; @@ -43,12 +44,12 @@ export function keyValueToMetadata({ export async function pinEcosystemMetadata( ecosystemId: UUID, ecosystemMainAccountId: AccountId, - recipients: (ProjectReceiver | SubListReceiver)[], + recipients: (ProjectReceiver | SubListReceiver | DeadlineReceiver)[], ): Promise { const {name, description, color, avatar} = await getEcosystemById(ecosystemId); - const dripListMetadata = { + const ecosystemMetadata = { driver: 'nft', describes: { driver: 'nft', @@ -63,9 +64,7 @@ export async function pinEcosystemMetadata( avatar, } as LatestVersion; - nftDriverAccountMetadataParser.parseLatest(dripListMetadata); - - const ipfsHash = await pinJSON(dripListMetadata); + const ipfsHash = await pinJSON(ecosystemMetadata); logger.info( `Ecosystem Main Account '${ecosystemMainAccountId}' metadata pinned to IPFS with hash '${ipfsHash}'.`, From 5b72ebc421878d97cea0a000d2b1052a4ca28776 Mon Sep 17 00:00:00 2001 From: jtourkos Date: Tue, 7 Oct 2025 13:42:23 +0300 Subject: [PATCH 6/6] refactor: update contract addresses --- .../contracts/contractsConfig.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/common/infrastructure/contracts/contractsConfig.ts b/src/common/infrastructure/contracts/contractsConfig.ts index ba35b6f..c023f96 100644 --- a/src/common/infrastructure/contracts/contractsConfig.ts +++ b/src/common/infrastructure/contracts/contractsConfig.ts @@ -5,11 +5,11 @@ type ContractsConfig = { DRIPS: OxString; CALLER: OxString; REPO_DRIVER: OxString; - REPO_SUB_ACCOUNT_DRIVER: OxString | undefined; // TODO: remove this when repo sub account driver is deployed on all chains. - REPO_DEADLINE_DRIVER: OxString | undefined; + REPO_SUB_ACCOUNT_DRIVER: OxString; + REPO_DEADLINE_DRIVER: OxString; NFT_DRIVER: OxString; IMMUTABLE_SPLITS: OxString; - NATIVE_TOKEN_UNWRAPPER: OxString | undefined; // TODO: remove this when native token unwrapper is deployed on all chains. + NATIVE_TOKEN_UNWRAPPER: OxString; }; export const contractsConfigByChainId: Record = { @@ -23,7 +23,7 @@ export const contractsConfigByChainId: Record = { IMMUTABLE_SPLITS: '0x1212975c0642B07F696080ec1916998441c2b774', NATIVE_TOKEN_UNWRAPPER: '0xa4d564894eb4b318e06adbc284295b6597a22019', REPO_SUB_ACCOUNT_DRIVER: '0xc219395880fa72e3ad9180b8878e0d39d144130b', - REPO_DEADLINE_DRIVER: undefined, + REPO_DEADLINE_DRIVER: '0x8324ea3538f12895c941a625b7f15df2d7dbfdff', }, // Polygon Amoy 80002: { @@ -33,9 +33,9 @@ export const contractsConfigByChainId: Record = { REPO_DRIVER: '0x54372850Db72915Fd9C5EC745683EB607b4a8642', NFT_DRIVER: '0xDafd9Ab96E62941808caa115D184D30A200FA777', IMMUTABLE_SPLITS: '0x65A48270e51A7aa901fD8fc42ab9cDddb50aff05', - NATIVE_TOKEN_UNWRAPPER: undefined, - REPO_SUB_ACCOUNT_DRIVER: undefined, - REPO_DEADLINE_DRIVER: undefined, + NATIVE_TOKEN_UNWRAPPER: '0x0000000000000000000000000000000000000000', + REPO_SUB_ACCOUNT_DRIVER: '0x0000000000000000000000000000000000000000', + REPO_DEADLINE_DRIVER: '0x0000000000000000000000000000000000000000', }, // Optimism Sepolia 11155420: { @@ -93,9 +93,9 @@ export const contractsConfigByChainId: Record = { REPO_DRIVER: '0x54372850Db72915Fd9C5EC745683EB607b4a8642', NFT_DRIVER: '0xDafd9Ab96E62941808caa115D184D30A200FA777', IMMUTABLE_SPLITS: '0x65A48270e51A7aa901fD8fc42ab9cDddb50aff05', - NATIVE_TOKEN_UNWRAPPER: undefined, - REPO_SUB_ACCOUNT_DRIVER: undefined, - REPO_DEADLINE_DRIVER: undefined, + NATIVE_TOKEN_UNWRAPPER: '0x0000000000000000000000000000000000000000', + REPO_SUB_ACCOUNT_DRIVER: '0x0000000000000000000000000000000000000000', + REPO_DEADLINE_DRIVER: '0x0000000000000000000000000000000000000000', }, // Filecoin 314: { @@ -117,7 +117,7 @@ export const contractsConfigByChainId: Record = { REPO_DRIVER: '0xe75f56B26857cAe06b455Bfc9481593Ae0FB4257', NFT_DRIVER: '0x2F23217A87cAf04ae586eed7a3d689f6C48498dB', IMMUTABLE_SPLITS: '0x96EC722e1338f08bbd469b80394eE118a0bc6753', - NATIVE_TOKEN_UNWRAPPER: undefined, + NATIVE_TOKEN_UNWRAPPER: '0x0000000000000000000000000000000000000000', REPO_SUB_ACCOUNT_DRIVER: '0x925a69f6d07ee4c753df139bcc2a946e1d1ee92a', REPO_DEADLINE_DRIVER: '0x0386b66e2b0106ff27ef26e84102ca78a5c0edef', },