diff --git a/scripts/src/commands/sol/bridge/index.ts b/scripts/src/commands/sol/bridge/index.ts index afd8932..2aac26a 100644 --- a/scripts/src/commands/sol/bridge/index.ts +++ b/scripts/src/commands/sol/bridge/index.ts @@ -5,6 +5,7 @@ import { solVaultCommand } from "./sol-vault.command"; import { bridgeCallCommand, bridgeSolCommand, + bridgeSolWithBcCommand, bridgeSplCommand, bridgeWrappedTokenCommand, wrapTokenCommand, @@ -20,6 +21,7 @@ bridgeCommand.addCommand(solVaultCommand); bridgeCommand.addCommand(bridgeCallCommand); bridgeCommand.addCommand(bridgeSolCommand); +bridgeCommand.addCommand(bridgeSolWithBcCommand); bridgeCommand.addCommand(bridgeSplCommand); bridgeCommand.addCommand(bridgeWrappedTokenCommand); bridgeCommand.addCommand(wrapTokenCommand); diff --git a/scripts/src/commands/sol/bridge/solana-to-base/bridge-sol-with-bc.command.ts b/scripts/src/commands/sol/bridge/solana-to-base/bridge-sol-with-bc.command.ts new file mode 100644 index 0000000..8fd3b6a --- /dev/null +++ b/scripts/src/commands/sol/bridge/solana-to-base/bridge-sol-with-bc.command.ts @@ -0,0 +1,98 @@ +import { Command } from "commander"; + +import { + getInteractiveConfirm, + getOrPromptEvmAddress, + getOrPromptDecimal, + getOrPromptFilePath, + getOrPromptDeployEnv, + getOrPromptHex, + getOrPromptInteger, + validateAndExecute, + getOrPromptHash, +} from "@internal/utils/cli"; +import { + argsSchema, + handleBridgeSolWithBc, +} from "./bridge-sol-with-bc.handler"; + +type CommanderOptions = { + deployEnv?: string; + to?: string; + amount?: string; + builderCode?: string; + feeBps?: string; + payerKp?: string; + payForRelay?: boolean; +}; + +async function collectInteractiveOptions( + options: CommanderOptions +): Promise { + let opts = { ...options }; + + if (!opts.deployEnv) { + opts.deployEnv = await getOrPromptDeployEnv(); + } + + opts.to = await getOrPromptEvmAddress( + opts.to, + "Enter user address on Base (recipient for hookData)" + ); + + opts.amount = await getOrPromptDecimal( + opts.amount, + "Enter amount to bridge (in SOL)", + 0.001 + ); + + opts.builderCode = await getOrPromptHash( + opts.builderCode, + "Enter builder code (bytes32, 0x followed by 64 hex chars)" + ); + + opts.feeBps = await getOrPromptInteger( + opts.feeBps, + "Enter fee in basis points (e.g., 100 for 1%)", + 0, + 10000 + ); + + opts.payerKp = await getOrPromptFilePath( + opts.payerKp, + "Enter payer keypair path (or 'config' for Solana CLI config)", + ["config"] + ); + + if (opts.payForRelay === undefined) { + opts.payForRelay = await getInteractiveConfirm( + "Pay for relaying the message to Base?", + true + ); + } + + return opts; +} + +export const bridgeSolWithBcCommand = new Command("bridge-sol-with-bc") + .description("Bridge SOL from Solana to Base with Builder Code attribution") + .option( + "--deploy-env ", + "Target deploy environment (testnet-alpha | testnet-prod | mainnet)" + ) + .option("--to
", "User address on Base (for hookData)") + .option("--amount ", "Amount to bridge in SOL") + .option( + "--builder-code ", + "Builder code (bytes32, 0x followed by 64 hex chars)" + ) + .option("--fee-bps ", "Fee in basis points (e.g., 100 for 1%)") + .option( + "--payer-kp ", + "Payer keypair: 'config' or custom payer keypair path" + ) + .option("--pay-for-relay", "Pay for relaying the message to Base") + .action(async (options) => { + const opts = await collectInteractiveOptions(options); + await validateAndExecute(argsSchema, opts, handleBridgeSolWithBc); + }); diff --git a/scripts/src/commands/sol/bridge/solana-to-base/bridge-sol-with-bc.handler.ts b/scripts/src/commands/sol/bridge/solana-to-base/bridge-sol-with-bc.handler.ts new file mode 100644 index 0000000..7f5eee5 --- /dev/null +++ b/scripts/src/commands/sol/bridge/solana-to-base/bridge-sol-with-bc.handler.ts @@ -0,0 +1,204 @@ +import { z } from "zod"; +import { + getProgramDerivedAddress, + type Instruction, + createSolanaRpc, +} from "@solana/kit"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { + toBytes, + isAddress as isEvmAddress, + encodeAbiParameters, + encodeFunctionData, +} from "viem"; + +import { + CallType, + fetchBridge, + getBridgeSolInstruction, +} from "@base/bridge/bridge"; + +import { logger } from "@internal/logger"; +import { FLYWHEEL_ABI } from "@internal/base/abi"; +import { + buildAndSendTransaction, + getSolanaCliConfigKeypairSigner, + getKeypairSignerFromPath, + getIdlConstant, + relayMessageToBase, + monitorMessageExecution, + buildPayForRelayInstruction, + outgoingMessagePubkey, + solVaultPubkey, +} from "@internal/sol"; +import { CONFIGS, DEPLOY_ENVS } from "@internal/constants"; + +export const argsSchema = z.object({ + deployEnv: z + .enum(DEPLOY_ENVS, { + message: + "Deploy environment must be 'testnet-alpha', 'testnet-prod', or 'mainnet'", + }) + .default("testnet-prod"), + to: z + .string() + .refine((value) => isEvmAddress(value), { + message: "Invalid Base/Ethereum address format", + }) + .brand<"baseAddress">(), + amount: z + .string() + .transform((val) => parseFloat(val)) + .refine((val) => !isNaN(val) && val > 0, { + message: "Amount must be a positive number", + }), + builderCode: z + .string() + .regex(/^0x[a-fA-F0-9]{64}$/, { + message: + "Builder code must be a valid bytes32 (0x followed by 64 hex characters)", + }) + .brand<"builderCode">(), + feeBps: z + .string() + .transform((val) => parseInt(val)) + .refine((val) => !isNaN(val) && val >= 0 && val <= 10000, { + message: "Fee BPS must be a number between 0 and 10000", + }), + payerKp: z + .union([z.literal("config"), z.string().brand<"payerKp">()]) + .default("config"), + payForRelay: z.boolean().default(true), +}); + +type Args = z.infer; +type PayerKpArg = Args["payerKp"]; + +export async function handleBridgeSolWithBc(args: Args): Promise { + try { + logger.info("--- Bridge SOL with Builder Code script ---"); + + const config = CONFIGS[args.deployEnv]; + const rpc = createSolanaRpc(config.solana.rpcUrl); + logger.info(`RPC URL: ${config.solana.rpcUrl}`); + + const payer = await resolvePayerKeypair(args.payerKp); + logger.info(`Payer: ${payer.address}`); + + const [bridgeAccountAddress] = await getProgramDerivedAddress({ + programAddress: config.solana.bridgeProgram, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + logger.info(`Bridge account: ${bridgeAccountAddress}`); + + const bridge = await fetchBridge(rpc, bridgeAccountAddress); + + const solVaultAddress = await solVaultPubkey(config.solana.bridgeProgram); + logger.info(`Sol Vault: ${solVaultAddress}`); + + // Calculate scaled amount (amount * 10^decimals) + const scaledAmount = BigInt(Math.floor(args.amount * Math.pow(10, 9))); + logger.info(`Amount: ${args.amount}`); + logger.info(`Scaled amount: ${scaledAmount}`); + + const { salt, pubkey: outgoingMessage } = await outgoingMessagePubkey( + config.solana.bridgeProgram + ); + logger.info(`Outgoing message: ${outgoingMessage}`); + + // Builder Code logic + logger.info(`User address (for hookData): ${args.to}`); + logger.info(`Builder code: ${args.builderCode}`); + logger.info(`Fee BPS: ${args.feeBps}`); + + // 1. Build hookData = abi.encode(user, code, feeBps) + const hookData = encodeAbiParameters( + [ + { type: "address", name: "user" }, + { type: "bytes32", name: "code" }, + { type: "uint16", name: "feeBps" }, + ], + [args.to as `0x${string}`, args.builderCode as `0x${string}`, args.feeBps] + ); + logger.info(`Hook data: ${hookData}`); + + // 2. Build call data for Flywheel.send(campaign, token, hookData) + const wSolAddress = config.base.wSol; + logger.info(`wSOL address: ${wSolAddress}`); + logger.info(`Flywheel address: ${config.base.flywheelContract}`); + logger.info(`Bridge campaign address: ${config.base.flywheelCampaign}`); + + const flywheelCallData = encodeFunctionData({ + abi: FLYWHEEL_ABI, + functionName: "send", + args: [config.base.flywheelCampaign, wSolAddress, hookData], + }); + logger.info(`Flywheel call data: ${flywheelCallData}`); + + // 3. Build the bridge instruction with call to Flywheel + const ixs: Instruction[] = [ + getBridgeSolInstruction( + { + // Accounts + payer, + from: payer, + gasFeeReceiver: bridge.data.gasConfig.gasFeeReceiver, + solVault: solVaultAddress, + bridge: bridgeAccountAddress, + outgoingMessage, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + outgoingMessageSalt: salt, + to: toBytes(config.base.flywheelCampaign), // Send to campaign, not user + amount: scaledAmount, + call: { + ty: CallType.Call, + to: toBytes(config.base.flywheelContract), + value: 0n, + data: Buffer.from(flywheelCallData.slice(2), "hex"), + }, + }, + { programAddress: config.solana.bridgeProgram } + ), + ]; + + if (args.payForRelay) { + ixs.push( + await buildPayForRelayInstruction( + args.deployEnv, + outgoingMessage, + payer + ) + ); + } + + logger.info("Sending transaction..."); + const signature = await buildAndSendTransaction( + { type: "deploy-env", value: args.deployEnv }, + ixs, + payer + ); + logger.success("Bridge SOL with Builder Code operation completed!"); + logger.success(`Signature: ${signature}`); + + if (args.payForRelay) { + await monitorMessageExecution(args.deployEnv, outgoingMessage); + } else { + await relayMessageToBase(args.deployEnv, outgoingMessage); + } + } catch (error) { + logger.error("Bridge SOL with Builder Code operation failed:", error); + throw error; + } +} + +async function resolvePayerKeypair(payerKpArg: PayerKpArg) { + if (payerKpArg === "config") { + logger.info("Using Solana CLI config for payer keypair"); + return await getSolanaCliConfigKeypairSigner(); + } + + logger.info(`Using custom payer keypair: ${payerKpArg}`); + return await getKeypairSignerFromPath(payerKpArg); +} diff --git a/scripts/src/commands/sol/bridge/solana-to-base/index.ts b/scripts/src/commands/sol/bridge/solana-to-base/index.ts index bd07db7..ba087cf 100644 --- a/scripts/src/commands/sol/bridge/solana-to-base/index.ts +++ b/scripts/src/commands/sol/bridge/solana-to-base/index.ts @@ -1,5 +1,6 @@ export * from "./bridge-call.command"; export * from "./bridge-sol.command"; +export * from "./bridge-sol-with-bc.command"; export * from "./bridge-spl.command"; export * from "./bridge-wrapped-token.command"; export * from "./wrap-token.command"; diff --git a/scripts/src/internal/base/abi/flywheel.abi.ts b/scripts/src/internal/base/abi/flywheel.abi.ts new file mode 100644 index 0000000..e5bbde3 --- /dev/null +++ b/scripts/src/internal/base/abi/flywheel.abi.ts @@ -0,0 +1,689 @@ +export const FLYWHEEL_ABI = [ + { inputs: [], stateMutability: "nonpayable", type: "constructor" }, + { inputs: [], name: "CampaignDoesNotExist", type: "error" }, + { inputs: [], name: "FailedDeployment", type: "error" }, + { + inputs: [ + { internalType: "uint256", name: "balance", type: "uint256" }, + { internalType: "uint256", name: "needed", type: "uint256" }, + ], + name: "InsufficientBalance", + type: "error", + }, + { inputs: [], name: "InsufficientCampaignFunds", type: "error" }, + { inputs: [], name: "InvalidCampaignStatus", type: "error" }, + { inputs: [], name: "Reentrancy", type: "error" }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + ], + name: "SendFailed", + type: "error", + }, + { inputs: [], name: "ZeroAddress", type: "error" }, + { inputs: [], name: "ZeroAmount", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "hooks", + type: "address", + }, + ], + name: "CampaignCreated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { indexed: false, internalType: "string", name: "uri", type: "string" }, + ], + name: "CampaignMetadataUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: false, + internalType: "enum Flywheel.CampaignStatus", + name: "oldStatus", + type: "uint8", + }, + { + indexed: false, + internalType: "enum Flywheel.CampaignStatus", + name: "newStatus", + type: "uint8", + }, + ], + name: "CampaignStatusUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { indexed: false, internalType: "bytes32", name: "key", type: "bytes32" }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "FeeAllocated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { indexed: false, internalType: "bytes32", name: "key", type: "bytes32" }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "FeeDistributed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "FeeSent", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { indexed: false, internalType: "bytes32", name: "key", type: "bytes32" }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "FeeTransferFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "FundsWithdrawn", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { indexed: false, internalType: "bytes32", name: "key", type: "bytes32" }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "PayoutAllocated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { indexed: false, internalType: "bytes32", name: "key", type: "bytes32" }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "PayoutDeallocated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { indexed: false, internalType: "bytes32", name: "key", type: "bytes32" }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "PayoutDistributed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "campaign", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + name: "PayoutSent", + type: "event", + }, + { + inputs: [], + name: "CAMPAIGN_IMPLEMENTATION", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "allocate", + outputs: [ + { + components: [ + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Allocation[]", + name: "allocations", + type: "tuple[]", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + ], + name: "allocatedFee", + outputs: [{ internalType: "uint256", name: "amount", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + ], + name: "allocatedPayout", + outputs: [{ internalType: "uint256", name: "amount", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "campaign", type: "address" }], + name: "campaignExists", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "campaign", type: "address" }], + name: "campaignHooks", + outputs: [{ internalType: "address", name: "hooks", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "campaign", type: "address" }], + name: "campaignStatus", + outputs: [ + { + internalType: "enum Flywheel.CampaignStatus", + name: "status", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "campaign", type: "address" }], + name: "campaignURI", + outputs: [{ internalType: "string", name: "uri", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "hooks", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "createCampaign", + outputs: [{ internalType: "address", name: "campaign", type: "address" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "deallocate", + outputs: [ + { + components: [ + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Allocation[]", + name: "allocations", + type: "tuple[]", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "distribute", + outputs: [ + { + components: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Distribution[]", + name: "distributions", + type: "tuple[]", + }, + { + components: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Distribution[]", + name: "fees", + type: "tuple[]", + }, + { internalType: "bool", name: "sendFeesNow", type: "bool" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "distributeFees", + outputs: [ + { + components: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Distribution[]", + name: "distributions", + type: "tuple[]", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "hooks", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "predictCampaignAddress", + outputs: [{ internalType: "address", name: "campaign", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "send", + outputs: [ + { + components: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Payout[]", + name: "payouts", + type: "tuple[]", + }, + { + components: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "bytes32", name: "key", type: "bytes32" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "bytes", name: "extraData", type: "bytes" }, + ], + internalType: "struct Flywheel.Distribution[]", + name: "fees", + type: "tuple[]", + }, + { internalType: "bool", name: "sendFeesNow", type: "bool" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + ], + name: "totalAllocatedFees", + outputs: [{ internalType: "uint256", name: "amount", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + ], + name: "totalAllocatedPayouts", + outputs: [{ internalType: "uint256", name: "amount", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "updateMetadata", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { + internalType: "enum Flywheel.CampaignStatus", + name: "newStatus", + type: "uint8", + }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "updateStatus", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "campaign", type: "address" }, + { internalType: "address", name: "token", type: "address" }, + { internalType: "bytes", name: "hookData", type: "bytes" }, + ], + name: "withdrawFunds", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +] as const; diff --git a/scripts/src/internal/base/abi/index.ts b/scripts/src/internal/base/abi/index.ts index 0394563..b093c27 100644 --- a/scripts/src/internal/base/abi/index.ts +++ b/scripts/src/internal/base/abi/index.ts @@ -1,2 +1,3 @@ export * from "./bridge.abi"; export * from "./bridge-validator.abi"; +export * from "./flywheel.abi"; diff --git a/scripts/src/internal/constants.ts b/scripts/src/internal/constants.ts index f4745d1..4366fd4 100644 --- a/scripts/src/internal/constants.ts +++ b/scripts/src/internal/constants.ts @@ -45,6 +45,8 @@ export type Config = { // Contracts bridgeContract: EvmAddress; counterContract: EvmAddress; + flywheelContract: EvmAddress; + flywheelCampaign: EvmAddress; // ERC20s erc20: EvmAddress; @@ -85,6 +87,8 @@ export const CONFIGS = { // Contracts bridgeContract: "0x64567a9147fa89B1edc987e36Eb6f4b6db71656b", counterContract: "0x5d3eB988Daa06151b68369cf957e917B4371d35d", + flywheelContract: "0x00000f14ad09382841db481403d1775adee1179f", + flywheelCampaign: "0x7626f7F9A574f526066acE9073518DaB1Bee038C", // ERC20s erc20: "0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9", @@ -123,6 +127,8 @@ export const CONFIGS = { // Contracts bridgeContract: "0x01824a90d32A69022DdAEcC6C5C14Ed08dB4EB9B", counterContract: "0x5d3eB988Daa06151b68369cf957e917B4371d35d", + flywheelContract: "0x00000f14ad09382841db481403d1775adee1179f", + flywheelCampaign: "0x7626f7F9A574f526066acE9073518DaB1Bee038C", // ERC20s erc20: "0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9", @@ -161,6 +167,8 @@ export const CONFIGS = { // Contracts bridgeContract: "0x3eff766C76a1be2Ce1aCF2B69c78bCae257D5188", counterContract: "0x", + flywheelContract: "0x", + flywheelCampaign: "0x", // ERC20s erc20: "0x4870D23984Dd663005EB8E2b616e4Ef62630183c", diff --git a/scripts/src/internal/sol/base-relayer.ts b/scripts/src/internal/sol/base-relayer.ts index 2204095..af2d2d5 100644 --- a/scripts/src/internal/sol/base-relayer.ts +++ b/scripts/src/internal/sol/base-relayer.ts @@ -48,7 +48,7 @@ export async function buildPayForRelayInstruction( // Arguments outgoingMessage: outgoingMessage, - gasLimit: 200_000n, + gasLimit: 2_000_000n, }, { programAddress: config.solana.baseRelayerProgram } );