Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clients/ts/src/bridge/generated/accounts/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,5 @@ export async function fetchAllMaybeBridge(
}

export function getBridgeSize(): number {
return 508;
return 528;
}
6 changes: 5 additions & 1 deletion clients/ts/src/bridge/generated/errors/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export const BRIDGE_ERROR__INVALID_GAS_COST_SCALER_DP = 0x3206; // 12806
export const BRIDGE_ERROR__INVALID_BLOCK_INTERVAL_REQUIREMENT = 0x3207; // 12807
/** CreationWithNonZeroTarget: Creation with non-zero target */
export const BRIDGE_ERROR__CREATION_WITH_NON_ZERO_TARGET = 0x3264; // 12900
/** ZeroAddress: Zero address */
export const BRIDGE_ERROR__ZERO_ADDRESS = 0x3265; // 12901

export type BridgeError =
| typeof BRIDGE_ERROR__ALREADY_EXECUTED
Expand Down Expand Up @@ -147,7 +149,8 @@ export type BridgeError =
| typeof BRIDGE_ERROR__TOO_MANY_SIGNERS
| typeof BRIDGE_ERROR__UNAUTHORIZED_CONFIG_UPDATE
| typeof BRIDGE_ERROR__UNAUTHORIZED_INITIALIZATION
| typeof BRIDGE_ERROR__UNUSED_PROOF_ELEMENTS_REMAINING;
| typeof BRIDGE_ERROR__UNUSED_PROOF_ELEMENTS_REMAINING
| typeof BRIDGE_ERROR__ZERO_ADDRESS;

let bridgeErrorMessages: Record<BridgeError, string> | undefined;
if (process.env.NODE_ENV !== 'production') {
Expand Down Expand Up @@ -196,6 +199,7 @@ if (process.env.NODE_ENV !== 'production') {
[BRIDGE_ERROR__UNAUTHORIZED_CONFIG_UPDATE]: `Unauthorized to update configuration`,
[BRIDGE_ERROR__UNAUTHORIZED_INITIALIZATION]: `Only the upgrade authority can initialize the bridge`,
[BRIDGE_ERROR__UNUSED_PROOF_ELEMENTS_REMAINING]: `Unused proof elements remaining`,
[BRIDGE_ERROR__ZERO_ADDRESS]: `Zero address`,
};
}

Expand Down
4 changes: 2 additions & 2 deletions clients/ts/src/bridge/generated/instructions/bridgeCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export type BridgeCallInput<
bridge: Address<TAccountBridge>;
/**
* The outgoing message account that stores the cross-chain call data.
* - Created fresh for each bridge call at a client-provided address (not a PDA)
* - Created fresh for each bridge call seeded by a client-provided salt
* - Payer funds the account creation
* - Space is DISCRIMINATOR_LEN + OutgoingMessage::space(...)` and is sized using
* the worst-case message variant to ensure sufficient capacity even for large payloads
Expand Down Expand Up @@ -276,7 +276,7 @@ export type ParsedBridgeCallInstruction<
bridge: TAccountMetas[3];
/**
* The outgoing message account that stores the cross-chain call data.
* - Created fresh for each bridge call at a client-provided address (not a PDA)
* - Created fresh for each bridge call seeded by a client-provided salt
* - Payer funds the account creation
* - Space is DISCRIMINATOR_LEN + OutgoingMessage::space(...)` and is sized using
* the worst-case message variant to ensure sufficient capacity even for large payloads
Expand Down
9 changes: 2 additions & 7 deletions clients/ts/src/bridge/generated/instructions/bridgeSol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,13 @@ export type BridgeSolInstructionData = {
discriminator: ReadonlyUint8Array;
outgoingMessageSalt: ReadonlyUint8Array;
to: ReadonlyUint8Array;
remoteToken: ReadonlyUint8Array;
amount: bigint;
call: Option<Call>;
};

export type BridgeSolInstructionDataArgs = {
outgoingMessageSalt: ReadonlyUint8Array;
to: ReadonlyUint8Array;
remoteToken: ReadonlyUint8Array;
amount: number | bigint;
call: OptionOrNullable<CallArgs>;
};
Expand All @@ -118,7 +116,6 @@ export function getBridgeSolInstructionDataEncoder(): Encoder<BridgeSolInstructi
['discriminator', fixEncoderSize(getBytesEncoder(), 8)],
['outgoingMessageSalt', fixEncoderSize(getBytesEncoder(), 32)],
['to', fixEncoderSize(getBytesEncoder(), 20)],
['remoteToken', fixEncoderSize(getBytesEncoder(), 20)],
['amount', getU64Encoder()],
['call', getOptionEncoder(getCallEncoder())],
]),
Expand All @@ -131,7 +128,6 @@ export function getBridgeSolInstructionDataDecoder(): Decoder<BridgeSolInstructi
['discriminator', fixDecoderSize(getBytesDecoder(), 8)],
['outgoingMessageSalt', fixDecoderSize(getBytesDecoder(), 32)],
['to', fixDecoderSize(getBytesDecoder(), 20)],
['remoteToken', fixDecoderSize(getBytesDecoder(), 20)],
['amount', getU64Decoder()],
['call', getOptionDecoder(getCallDecoder())],
]);
Expand Down Expand Up @@ -170,7 +166,7 @@ export type BridgeSolInput<
gasFeeReceiver: Address<TAccountGasFeeReceiver>;
/**
* The SOL vault account that holds locked tokens for the specific remote token.
* - Uses PDA with SOL_VAULT_SEED and remote_token for deterministic address
* - Uses PDA with SOL_VAULT_SEED for deterministic address
* - Mutable to receive the locked SOL tokens
* - Each remote token has its own dedicated vault
*
Expand All @@ -196,7 +192,6 @@ export type BridgeSolInput<
systemProgram?: Address<TAccountSystemProgram>;
outgoingMessageSalt: BridgeSolInstructionDataArgs['outgoingMessageSalt'];
to: BridgeSolInstructionDataArgs['to'];
remoteToken: BridgeSolInstructionDataArgs['remoteToken'];
amount: BridgeSolInstructionDataArgs['amount'];
call: BridgeSolInstructionDataArgs['call'];
};
Expand Down Expand Up @@ -305,7 +300,7 @@ export type ParsedBridgeSolInstruction<
gasFeeReceiver: TAccountMetas[2];
/**
* The SOL vault account that holds locked tokens for the specific remote token.
* - Uses PDA with SOL_VAULT_SEED and remote_token for deterministic address
* - Uses PDA with SOL_VAULT_SEED for deterministic address
* - Mutable to receive the locked SOL tokens
* - Each remote token has its own dedicated vault
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,12 @@ export type BridgeSolWithBufferedCallInstructionData = {
discriminator: ReadonlyUint8Array;
outgoingMessageSalt: ReadonlyUint8Array;
to: ReadonlyUint8Array;
remoteToken: ReadonlyUint8Array;
amount: bigint;
};

export type BridgeSolWithBufferedCallInstructionDataArgs = {
outgoingMessageSalt: ReadonlyUint8Array;
to: ReadonlyUint8Array;
remoteToken: ReadonlyUint8Array;
amount: number | bigint;
};

Expand All @@ -117,7 +115,6 @@ export function getBridgeSolWithBufferedCallInstructionDataEncoder(): FixedSizeE
['discriminator', fixEncoderSize(getBytesEncoder(), 8)],
['outgoingMessageSalt', fixEncoderSize(getBytesEncoder(), 32)],
['to', fixEncoderSize(getBytesEncoder(), 20)],
['remoteToken', fixEncoderSize(getBytesEncoder(), 20)],
['amount', getU64Encoder()],
]),
(value) => ({
Expand All @@ -132,7 +129,6 @@ export function getBridgeSolWithBufferedCallInstructionDataDecoder(): FixedSizeD
['discriminator', fixDecoderSize(getBytesDecoder(), 8)],
['outgoingMessageSalt', fixDecoderSize(getBytesDecoder(), 32)],
['to', fixDecoderSize(getBytesDecoder(), 20)],
['remoteToken', fixDecoderSize(getBytesDecoder(), 20)],
['amount', getU64Decoder()],
]);
}
Expand Down Expand Up @@ -172,7 +168,7 @@ export type BridgeSolWithBufferedCallInput<
gasFeeReceiver: Address<TAccountGasFeeReceiver>;
/**
* The SOL vault account that holds locked tokens for the specific remote token.
* - PDA of this program using `[SOL_VAULT_SEED, remote_token]`
* - PDA of this program using `[SOL_VAULT_SEED]`
* - Mutable to receive the locked SOL
* - Each remote token has its own dedicated vault
*
Expand Down Expand Up @@ -203,7 +199,6 @@ export type BridgeSolWithBufferedCallInput<
systemProgram?: Address<TAccountSystemProgram>;
outgoingMessageSalt: BridgeSolWithBufferedCallInstructionDataArgs['outgoingMessageSalt'];
to: BridgeSolWithBufferedCallInstructionDataArgs['to'];
remoteToken: BridgeSolWithBufferedCallInstructionDataArgs['remoteToken'];
amount: BridgeSolWithBufferedCallInstructionDataArgs['amount'];
};

Expand Down Expand Up @@ -323,7 +318,7 @@ export type ParsedBridgeSolWithBufferedCallInstruction<
gasFeeReceiver: TAccountMetas[2];
/**
* The SOL vault account that holds locked tokens for the specific remote token.
* - PDA of this program using `[SOL_VAULT_SEED, remote_token]`
* - PDA of this program using `[SOL_VAULT_SEED]`
* - Mutable to receive the locked SOL
* - Each remote token has its own dedicated vault
*
Expand Down
19 changes: 0 additions & 19 deletions clients/ts/src/bridge/generated/types/finalizeBridgeSol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@

import {
combineCodec,
fixDecoderSize,
fixEncoderSize,
getAddressDecoder,
getAddressEncoder,
getBytesDecoder,
getBytesEncoder,
getStructDecoder,
getStructEncoder,
getU64Decoder,
Expand All @@ -22,7 +18,6 @@ import {
type FixedSizeCodec,
type FixedSizeDecoder,
type FixedSizeEncoder,
type ReadonlyUint8Array,
} from '@solana/kit';

/**
Expand All @@ -33,12 +28,6 @@ import {
* the recipient when finalized.
*/
export type FinalizeBridgeSol = {
/**
* The 20-byte EVM address on Base of the ERC-20 token that represents SOL for this bridge.
* Used as a seed to derive the SOL vault PDA that escrows SOL for this mapping.
* This identifier names the vault even though the asset released here is native SOL.
*/
remoteToken: ReadonlyUint8Array;
/**
* The Solana public key of the recipient who will receive the SOL.
* This must match the intended recipient specified in the original bridge message.
Expand All @@ -52,12 +41,6 @@ export type FinalizeBridgeSol = {
};

export type FinalizeBridgeSolArgs = {
/**
* The 20-byte EVM address on Base of the ERC-20 token that represents SOL for this bridge.
* Used as a seed to derive the SOL vault PDA that escrows SOL for this mapping.
* This identifier names the vault even though the asset released here is native SOL.
*/
remoteToken: ReadonlyUint8Array;
/**
* The Solana public key of the recipient who will receive the SOL.
* This must match the intended recipient specified in the original bridge message.
Expand All @@ -72,15 +55,13 @@ export type FinalizeBridgeSolArgs = {

export function getFinalizeBridgeSolEncoder(): FixedSizeEncoder<FinalizeBridgeSolArgs> {
return getStructEncoder([
['remoteToken', fixEncoderSize(getBytesEncoder(), 20)],
['to', getAddressEncoder()],
['amount', getU64Encoder()],
]);
}

export function getFinalizeBridgeSolDecoder(): FixedSizeDecoder<FinalizeBridgeSol> {
return getStructDecoder([
['remoteToken', fixDecoderSize(getBytesDecoder(), 20)],
['to', getAddressDecoder()],
['amount', getU64Decoder()],
]);
Expand Down
19 changes: 17 additions & 2 deletions clients/ts/src/bridge/generated/types/protocolConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@

import {
combineCodec,
fixDecoderSize,
fixEncoderSize,
getBytesDecoder,
getBytesEncoder,
getStructDecoder,
getStructEncoder,
getU64Decoder,
getU64Encoder,
type FixedSizeCodec,
type FixedSizeDecoder,
type FixedSizeEncoder,
type ReadonlyUint8Array,
} from '@solana/kit';

export type ProtocolConfig = {
Expand All @@ -23,6 +28,8 @@ export type ProtocolConfig = {
* submitted output root must be a multiple of this number.
*/
blockIntervalRequirement: bigint;
/** The Base evm address of SOL */
remoteSolAddress: ReadonlyUint8Array;
};

export type ProtocolConfigArgs = {
Expand All @@ -31,14 +38,22 @@ export type ProtocolConfigArgs = {
* submitted output root must be a multiple of this number.
*/
blockIntervalRequirement: number | bigint;
/** The Base evm address of SOL */
remoteSolAddress: ReadonlyUint8Array;
};

export function getProtocolConfigEncoder(): FixedSizeEncoder<ProtocolConfigArgs> {
return getStructEncoder([['blockIntervalRequirement', getU64Encoder()]]);
return getStructEncoder([
['blockIntervalRequirement', getU64Encoder()],
['remoteSolAddress', fixEncoderSize(getBytesEncoder(), 20)],
]);
}

export function getProtocolConfigDecoder(): FixedSizeDecoder<ProtocolConfig> {
return getStructDecoder([['blockIntervalRequirement', getU64Decoder()]]);
return getStructDecoder([
['blockIntervalRequirement', getU64Decoder()],
['remoteSolAddress', fixDecoderSize(getBytesDecoder(), 20)],
]);
}

export function getProtocolConfigCodec(): FixedSizeCodec<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022";
import { toBytes, toHex } from "viem";

import {
fetchBridge,
fetchIncomingMessage,
getRelayMessageInstruction,
type BridgeBaseToSolanaStateIncomingMessageMessage,
Expand Down Expand Up @@ -254,19 +255,15 @@ async function messageTransferSolAccounts(
) {
logger.info("SOL transfer detected");

const { remoteToken, to, amount } = message.fields[0];
const { to, amount } = message.fields[0];

logger.info(`SOL transfer:`);
logger.info(` Remote token: 0x${remoteToken.toHex()}`);
logger.info(` To: ${to}`);
logger.info(` Amount: ${amount}`);

const [solVaultPda] = await getProgramDerivedAddress({
programAddress: solanaBridge,
seeds: [
Buffer.from(getIdlConstant("SOL_VAULT_SEED")),
Buffer.from(remoteToken),
],
seeds: [Buffer.from(getIdlConstant("SOL_VAULT_SEED"))],
});
logger.info(`SOL vault PDA: ${solVaultPda}`);

Expand Down
11 changes: 11 additions & 0 deletions scripts/src/commands/sol/bridge/initialize.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { text, isCancel, cancel } from "@clack/prompts";
import {
getOrPromptBigint,
getOrPromptSolanaAddress,
getOrPromptEvmAddress,
getOrPromptEvmAddressList,
getOrPromptFilePath,
validateAndExecute,
Expand All @@ -15,6 +16,7 @@ type CommanderOptions = {
rpcUrl?: string;
payerKp?: string;
guardian?: string;
remoteSolAddress?: string;
eip1559Target?: string;
eip1559Denominator?: string;
eip1559WindowDurationSeconds?: string;
Expand Down Expand Up @@ -77,6 +79,11 @@ async function collectInteractiveOptions(
["payer"]
);

opts.remoteSolAddress = await getOrPromptEvmAddress(
opts.remoteSolAddress,
"Enter remote SOL address (EVM address)"
);

opts.eip1559Target = await getOrPromptBigint(
opts.eip1559Target,
"Enter EIP-1559 target (bigint)"
Expand Down Expand Up @@ -157,6 +164,10 @@ export const initializeCommand = new Command("initialize")
"--guardian <address>",
"Guardian address: 'payer' or Solana public key"
)
.option(
"--remote-sol-address <address>",
"Remote SOL address (EVM address)"
)
.option("--eip1559-target <uint>", "EIP-1559 target (bigint)")
.option("--eip1559-denominator <uint>", "EIP-1559 denominator (bigint)")
.option(
Expand Down
8 changes: 8 additions & 0 deletions scripts/src/commands/sol/bridge/initialize.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const gasFlatSchema = z.object({

const protocolFlatSchema = z.object({
protocolBlockIntervalRequirement: bigintSchema,
remoteSolAddress: evmAddressSchema,
});

const bufferFlatSchema = z.object({
Expand Down Expand Up @@ -142,6 +143,7 @@ export async function handleInitialize(args: InitializeArgs): Promise<void> {

const protocolConfig: ProtocolConfig = {
blockIntervalRequirement: args.protocolBlockIntervalRequirement,
remoteSolAddress: toBytes(args.remoteSolAddress),
};

const bufferConfig: BufferConfig = {
Expand Down Expand Up @@ -298,6 +300,12 @@ async function assertInitialized(
) {
throw new Error("Protocol config blockIntervalRequirement mismatch!");
}
if (
toHex(new Uint8Array(bridgeData.data.protocolConfig.remoteSolAddress)) !==
toHex(new Uint8Array(protocolConfig.remoteSolAddress))
) {
throw new Error("Protocol config remoteSolAddress mismatch!");
}

// Buffer config confirmation
if (
Expand Down
Loading