From 9b3265e48ed0610930124ddabfb5c41fcab0d100 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 7 Jul 2025 21:58:00 -0400 Subject: [PATCH 1/3] onchain MMR optimizations (#9) --- base/src/libraries/MessageStorageLib.sol | 76 ++++++++++--------- .../bridge/src/base_to_solana/internal/mmr.rs | 20 ++--- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/base/src/libraries/MessageStorageLib.sol b/base/src/libraries/MessageStorageLib.sol index 915d014f..f8137d15 100644 --- a/base/src/libraries/MessageStorageLib.sol +++ b/base/src/libraries/MessageStorageLib.sol @@ -288,7 +288,7 @@ library MessageStorageLib { /// /// @param leafIndex The 0-indexed position of the leaf to prove. /// - /// @return Hashes of other mountain peaks in right-to-left order. + /// @return Hashes of other mountain peaks in left-to-right order. function _collectOtherPeaks(uint64 leafIndex) private view returns (bytes32[] memory) { MessageStorageLibStorage storage $ = getMessageStorageLibStorage(); @@ -298,6 +298,7 @@ library MessageStorageLib { uint64 leafOffset = 0; uint256 maxHeight = _calculateMaxPossibleHeight($.lastOutgoingNonce); + // Collect peaks in left-to-right order (largest to smallest mountain) for (uint256 h = maxHeight + 1; h > 0; h--) { uint256 height = h - 1; @@ -315,11 +316,21 @@ library MessageStorageLib { } } - // Copy to exact size array in reverse order (right-to-left for peak bagging) - bytes32[] memory peaks = new bytes32[](peakCount); - for (uint256 i = 0; i < peakCount; i++) { - peaks[i] = tempPeaks[peakCount - 1 - i]; + // Use assembly to create exact size array without reversal + bytes32[] memory peaks; + assembly ("memory-safe") { + peaks := mload(0x40) + mstore(peaks, peakCount) + mstore(0x40, add(peaks, add(0x20, mul(peakCount, 0x20)))) + + // Copy elements in natural order (left-to-right) + for { let i := 0 } lt(i, peakCount) { i := add(i, 1) } { + let sourceIndex := add(tempPeaks, add(0x20, mul(i, 0x20))) + let destIndex := add(peaks, add(0x20, mul(i, 0x20))) + mstore(destIndex, mload(sourceIndex)) + } } + return peaks; } @@ -349,21 +360,21 @@ library MessageStorageLib { return _hashPeaksSequentially(peakIndices); } - /// @notice Hashes all peaks sequentially from right to left. + /// @notice Hashes all peaks sequentially from left to right. /// - /// @param peakIndices Array of peak node indices (ordered from rightmost to leftmost). + /// @param peakIndices Array of peak node indices (ordered from leftmost to rightmost). /// /// @return The final root hash after hashing all peaks. function _hashPeaksSequentially(uint256[] memory peakIndices) private view returns (bytes32) { MessageStorageLibStorage storage $ = getMessageStorageLibStorage(); - // Start with the rightmost peak (first in our reversed list) + // Start with the leftmost peak (first in our left-to-right list) bytes32 currentRoot = $.nodes[peakIndices[0]]; - // Sequentially hash with the next peak to the left + // Sequentially hash with the next peak to the right for (uint256 i = 1; i < peakIndices.length; i++) { bytes32 nextPeakHash = $.nodes[peakIndices[i]]; - currentRoot = _hashInternalNode(nextPeakHash, currentRoot); + currentRoot = _hashInternalNode(currentRoot, nextPeakHash); } return currentRoot; @@ -371,7 +382,7 @@ library MessageStorageLib { /// @notice Gets the indices of all peak nodes in the MMR. /// - /// @return The indices of the peak nodes ordered from rightmost to leftmost. + /// @return The indices of the peak nodes ordered from leftmost to rightmost. function _getPeakNodeIndices() private view returns (uint256[] memory) { MessageStorageLibStorage storage $ = getMessageStorageLibStorage(); return _getPeakNodeIndicesForLeafCount($.lastOutgoingNonce); @@ -381,7 +392,7 @@ library MessageStorageLib { /// /// @param leafCount The number of leaves to calculate peaks for. /// - /// @return The indices of the peak nodes ordered from rightmost to leftmost. + /// @return The indices of the peak nodes ordered from leftmost to rightmost. function _getPeakNodeIndicesForLeafCount(uint64 leafCount) private pure returns (uint256[] memory) { if (leafCount == 0) { return new uint256[](0); @@ -390,25 +401,38 @@ library MessageStorageLib { uint256[] memory tempPeakIndices = new uint256[](_MAX_PEAKS); uint256 peakCount = 0; uint256 nodeOffset = 0; - uint64 remainingLeaves = leafCount; uint256 maxHeight = _calculateMaxPossibleHeight(leafCount); - // Process each possible height from largest to smallest + // Process each possible height from largest to smallest (left-to-right) for (uint256 height = maxHeight + 1; height > 0; height--) { uint256 currentHeight = height - 1; - if (_hasCompleteMountainAtHeight(remainingLeaves, currentHeight)) { + if (_hasCompleteMountainAtHeight(leafCount, currentHeight)) { uint256 peakIndex = _calculatePeakIndex(nodeOffset, currentHeight); tempPeakIndices[peakCount] = peakIndex; peakCount++; // Update state for next iteration nodeOffset += _calculateMountainSize(currentHeight); - remainingLeaves -= uint64(1 << currentHeight); } } - return _reversePeakIndices(tempPeakIndices, peakCount); + // Use assembly to create exact size array without reversal + uint256[] memory peakIndices; + assembly ("memory-safe") { + peakIndices := mload(0x40) + mstore(peakIndices, peakCount) + mstore(0x40, add(peakIndices, add(0x20, mul(peakCount, 0x20)))) + + // Copy elements in natural order (left-to-right) + for { let i := 0 } lt(i, peakCount) { i := add(i, 1) } { + let sourceIndex := add(tempPeakIndices, add(0x20, mul(i, 0x20))) + let destIndex := add(peakIndices, add(0x20, mul(i, 0x20))) + mstore(destIndex, mload(sourceIndex)) + } + } + + return peakIndices; } /// @notice Checks if nodes should be merged at the given height based on leaf count. @@ -479,23 +503,7 @@ library MessageStorageLib { return (1 << (height + 1)) - 1; } - /// @notice Reverses the peak indices array to get the correct order. - /// - /// @param tempPeakIndices Temporary array containing peak indices. - /// @param peakCount Number of peaks found. - /// - /// @return Reversed array of peak indices. - function _reversePeakIndices(uint256[] memory tempPeakIndices, uint256 peakCount) - private - pure - returns (uint256[] memory) - { - uint256[] memory peakIndices = new uint256[](peakCount); - for (uint256 i = 0; i < peakCount; i++) { - peakIndices[i] = tempPeakIndices[peakCount - 1 - i]; - } - return peakIndices; - } + /// @notice Hashes two node hashes together. /// diff --git a/solana/programs/bridge/src/base_to_solana/internal/mmr.rs b/solana/programs/bridge/src/base_to_solana/internal/mmr.rs index 04a2fa8a..63e7165d 100644 --- a/solana/programs/bridge/src/base_to_solana/internal/mmr.rs +++ b/solana/programs/bridge/src/base_to_solana/internal/mmr.rs @@ -10,7 +10,7 @@ pub struct Proof { /// Verifies an MMR proof. /// /// The proof consists of sibling hashes along the path from the leaf to its -/// mountain's peak, followed by the hashes of all other mountain peaks (right-to-left). +/// mountain's peak, followed by the hashes of all other mountain peaks (left-to-right). /// /// # Arguments /// * `proof` - The proof elements. @@ -111,9 +111,9 @@ fn calculate_root_from_proof( let mut all_peak_hashes: Vec<[u8; 32]> = Vec::new(); let mut remaining_proof_idx = proof_idx_offset; - // Peaks are needed in right-to-left order for bagging. - // The `mountains` vector is currently left-to-right. - for (_height, _num_leaves, is_leafs_m) in mountains.iter().rev() { + // Peaks are needed in left-to-right order for bagging. + // The `mountains` vector is already in left-to-right order. + for (_height, _num_leaves, is_leafs_m) in mountains.iter() { if *is_leafs_m { all_peak_hashes.push(leaf_mountain_peak_hash); } else { @@ -132,8 +132,8 @@ fn calculate_root_from_proof( MmrError::UnusedProofElementsRemaining ); - // 4. Bag the peaks (right-to-left). - // `all_peak_hashes` is already in right-to-left mountain order because we iterated `mountains.iter().rev()`. + // 4. Bag the peaks (left-to-right). + // `all_peak_hashes` is already in left-to-right mountain order. if all_peak_hashes.is_empty() { // Should be caught by total_leaf_count == 0 earlier, but as a safeguard. require!(total_leaf_count == 0, MmrError::NoPeaksFoundForNonEmptyMmr); @@ -142,12 +142,12 @@ fn calculate_root_from_proof( return Ok([0u8; 32]); } - let mut current_root = all_peak_hashes[0]; // Start with the rightmost peak. + let mut current_root = all_peak_hashes[0]; // Start with the leftmost peak. for peak_hash in all_peak_hashes.iter().skip(1) { - // next_peak_hash is to the left of current_root. + // next_peak_hash is to the right of current_root. // Hashing order for bagging: H(LeftPeak, H(MiddlePeak, RightPeak)) - // So, current_root is the right operand, all_peak_hashes[i] is the left. - current_root = commutative_keccak256(*peak_hash, current_root); + // So, current_root is the left operand, all_peak_hashes[i] is the right. + current_root = commutative_keccak256(current_root, *peak_hash); } Ok(current_root) From 9da9a06ae48f8f2e6f6e8dd64090603be584db12 Mon Sep 17 00:00:00 2001 From: xenoliss Date: Thu, 10 Jul 2025 14:43:17 +0200 Subject: [PATCH 2/3] Remove Anchor Scripts (#11) * chore(solana): clean scripts * chore: redeploy tokens --- .gitignore | 4 +- base/Makefile | 44 +- .../84532/run-1752076599.json | 93 ++ .../84532/run-1752077024.json | 93 ++ .../84532/run-1752077085.json | 93 ++ .../CreateToken.s.sol/84532/run-latest.json | 86 +- base/deployments/base_sepolia.json | 6 +- base/deployments/base_sepolia_prod.json | 6 +- base/script/actions/CreateToken.s.sol | 8 +- base/test/mocks/Counter.sol | 4 + base/test/mocks/CounterValue.sol | 21 - solana/Anchor.toml | 28 +- solana/Cargo.toml | 1 + solana/bun.lock | 345 +++++- solana/bun.lockb | Bin 37570 -> 0 bytes solana/idl.ts | 1064 +++++++++++++++++ solana/migrations/deploy.ts | 12 - solana/package.json | 34 +- solana/scripts/addresses.ts | 5 - .../scripts/base-to-solana/prove-message.ts | 169 --- .../scripts/base-to-solana/relay-message.ts | 272 ----- solana/scripts/constants.ts | 81 +- solana/scripts/generate-client.ts | 31 + solana/scripts/generate-idl.ts | 35 + solana/scripts/initialize.ts | 39 - .../onchain/base-to-solana/prove-message.ts | 179 +++ .../onchain/base-to-solana/relay-message.ts | 283 +++++ solana/scripts/onchain/initialize.ts | 50 + .../solana-to-base/bridge-call-value.ts | 101 ++ .../onchain/solana-to-base/bridge-call.ts | 79 ++ .../onchain/solana-to-base/bridge-sol.ts | 82 ++ .../onchain/solana-to-base/bridge-spl.ts | 90 ++ .../solana-to-base/bridge-wrapped-token.ts | 74 ++ .../onchain/solana-to-base/wrap-token.ts | 103 ++ solana/scripts/onchain/spl/create-ata.ts | 71 ++ solana/scripts/onchain/spl/create-mint.ts | 65 + solana/scripts/onchain/spl/mint.ts | 40 + .../scripts/{ => onchain}/utils/bridge.abi.ts | 0 .../{ => onchain}/utils/buffer-reader.ts | 6 +- .../{ => onchain}/utils/deserializer.ts | 63 +- solana/scripts/onchain/utils/transaction.ts | 78 ++ solana/scripts/program/build.ts | 65 + solana/scripts/program/deploy.ts | 43 + .../solana-to-base/bridge-call-value.ts | 104 -- solana/scripts/solana-to-base/bridge-call.ts | 62 - solana/scripts/solana-to-base/bridge-sol.ts | 79 -- solana/scripts/solana-to-base/bridge-spl.ts | 92 -- .../solana-to-base/bridge-wrapped-token.ts | 82 -- solana/scripts/solana-to-base/wrap-token.ts | 82 -- solana/scripts/spl/create-mint.ts | 30 - solana/scripts/spl/create-user-ata.ts | 36 - solana/scripts/spl/mint-spl.ts | 46 - solana/scripts/utils/argv.ts | 16 + solana/scripts/utils/confirm-tx.ts | 18 - solana/scripts/utils/env.ts | 7 - solana/scripts/utils/file.ts | 7 + .../utils/{constants.ts => idl-constants.ts} | 58 +- solana/scripts/utils/keypair.ts | 7 + solana/scripts/utils/pubkey-to-bytes32.ts | 8 +- 59 files changed, 3382 insertions(+), 1398 deletions(-) create mode 100644 base/broadcast/CreateToken.s.sol/84532/run-1752076599.json create mode 100644 base/broadcast/CreateToken.s.sol/84532/run-1752077024.json create mode 100644 base/broadcast/CreateToken.s.sol/84532/run-1752077085.json delete mode 100644 base/test/mocks/CounterValue.sol delete mode 100755 solana/bun.lockb create mode 100644 solana/idl.ts delete mode 100644 solana/migrations/deploy.ts delete mode 100644 solana/scripts/addresses.ts delete mode 100644 solana/scripts/base-to-solana/prove-message.ts delete mode 100644 solana/scripts/base-to-solana/relay-message.ts create mode 100644 solana/scripts/generate-client.ts create mode 100644 solana/scripts/generate-idl.ts delete mode 100644 solana/scripts/initialize.ts create mode 100644 solana/scripts/onchain/base-to-solana/prove-message.ts create mode 100644 solana/scripts/onchain/base-to-solana/relay-message.ts create mode 100644 solana/scripts/onchain/initialize.ts create mode 100644 solana/scripts/onchain/solana-to-base/bridge-call-value.ts create mode 100644 solana/scripts/onchain/solana-to-base/bridge-call.ts create mode 100644 solana/scripts/onchain/solana-to-base/bridge-sol.ts create mode 100644 solana/scripts/onchain/solana-to-base/bridge-spl.ts create mode 100644 solana/scripts/onchain/solana-to-base/bridge-wrapped-token.ts create mode 100644 solana/scripts/onchain/solana-to-base/wrap-token.ts create mode 100644 solana/scripts/onchain/spl/create-ata.ts create mode 100644 solana/scripts/onchain/spl/create-mint.ts create mode 100644 solana/scripts/onchain/spl/mint.ts rename solana/scripts/{ => onchain}/utils/bridge.abi.ts (100%) rename solana/scripts/{ => onchain}/utils/buffer-reader.ts (84%) rename solana/scripts/{ => onchain}/utils/deserializer.ts (68%) create mode 100644 solana/scripts/onchain/utils/transaction.ts create mode 100644 solana/scripts/program/build.ts create mode 100644 solana/scripts/program/deploy.ts delete mode 100644 solana/scripts/solana-to-base/bridge-call-value.ts delete mode 100644 solana/scripts/solana-to-base/bridge-call.ts delete mode 100644 solana/scripts/solana-to-base/bridge-sol.ts delete mode 100644 solana/scripts/solana-to-base/bridge-spl.ts delete mode 100644 solana/scripts/solana-to-base/bridge-wrapped-token.ts delete mode 100644 solana/scripts/solana-to-base/wrap-token.ts delete mode 100644 solana/scripts/spl/create-mint.ts delete mode 100644 solana/scripts/spl/create-user-ata.ts delete mode 100644 solana/scripts/spl/mint-spl.ts create mode 100644 solana/scripts/utils/argv.ts delete mode 100644 solana/scripts/utils/confirm-tx.ts delete mode 100644 solana/scripts/utils/env.ts create mode 100644 solana/scripts/utils/file.ts rename solana/scripts/utils/{constants.ts => idl-constants.ts} (50%) create mode 100644 solana/scripts/utils/keypair.ts diff --git a/.gitignore b/.gitignore index 1ef894e9..e9aba673 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ node_modules test-ledger .yarn -.env \ No newline at end of file +.env +clients/ +keypairs/ \ No newline at end of file diff --git a/base/Makefile b/base/Makefile index b6cd415d..d0c451ff 100644 --- a/base/Makefile +++ b/base/Makefile @@ -3,23 +3,23 @@ BASE_RPC=https://base-sepolia.cbhq.net # BASE_RPC=https://mainnet.base.org -# Devnet Alpha Addresses +# # Devnet Alpha Addresses # BRIDGE = 0xfcde89DFe9276Ec059d68e43759a226f0961426F -# LOCAL_SOL = 0x314752245b830F3FEF1BE33Eaf16fF510Ba769a4 +# LOCAL_SOL = 0x4D3210A178De60668986eecfF4eC0B2508eEE1B2 # REMOTE_SOL = 0x069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000 -# LOCAL_SPL = 0x124229C60213709087c408ffe33D2b1142F91125 -# REMOTE_SPL = 0xf0f9a72753742be125e994a65ce0b2f9938e653fa5252e3f71277f128d85250d -# USER_SPL_ATA = 0x5743bace9b76bbf1bb9633a6a7f4ee4175d9a0c0745f4a2e9fd319244951d2fd +# LOCAL_SPL = 0xBc4027074e544Be820b1a16Bf4F4f7c626D61032 +# REMOTE_SPL = 0x6ccf56ff18093bc61462cd67c8aa86216fbad049e41d9bffa0856e5f34af5498 +# USER_SPL_ATA = 0xfb914beced28209b37284ca10566a11e4f8296c3314d502898cf76a04a257f24 # LOCAL_ERC20 = 0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9 -# REMOTE_ERC20 = 0x8c3446d39ffd65385d0ac81cdb48a47b6a7bb784e2ee8951b303629917a11c2c -# USER_REMOTE_ERC20_ATA = 0x9241988c16b2b21a00f7e8ad06c207f831017d69440c63be243442cdb63496bf +# REMOTE_ERC20 = 0xf91b492762157f10e66ba3e1254865e8d03497b47ea87a0d31768bde2ce4c7c4 +# USER_REMOTE_ERC20_ATA = 0x04268c7125e7ae1323905bac148591895f5ca23e517609b672b6b468c82d21a1 # LOCAL_ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE -# REMOTE_ETH = 0xcd7192592d77eee4f337b25393219215aa866d9b8addfc11da5853555ba9cefa -# USER_REMOTE_ETH_ATA = 0xde099694c85728a200685f2911d518ac69ab5619c6a74524f1d16eebc9a03620 +# REMOTE_ETH = 0xaa769890975f111ac07c870716b82151177b153b8d0c861c72ba89a979a0fc55 +# USER_REMOTE_ETH_ATA = 0xe43c7b73480e17628a38b5a51716fcc181396e0f3f943906215f23f32d4b76b6 # SOLANA_SOL_RECEIVER = 0x82c9f09a109bce580bb82c13c310689fd00e2225f8dd22015271620ecc035221 #################################################################################################### @@ -27,23 +27,23 @@ BASE_RPC=https://base-sepolia.cbhq.net # Devnet Prod Addresses BRIDGE = 0x96BB7fE0B5927CD604B1CfcaD4E16bB82bd1cc11 -LOCAL_SOL = 0x10B1A14E40a951777f4180875B2aBe03Ff5DEbae +LOCAL_SOL = 0x4D3210A178De60668986eecfF4eC0B2508eEE1B2 REMOTE_SOL = 0x069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000 -LOCAL_SPL = 0x26703A110c7F7834B8de61657182bF03b5c5b5a0 -REMOTE_SPL = 0xf0f9a72753742be125e994a65ce0b2f9938e653fa5252e3f71277f128d85250d -USER_SPL_ATA = 0x5743bace9b76bbf1bb9633a6a7f4ee4175d9a0c0745f4a2e9fd319244951d2fd +LOCAL_SPL = 0xe545c49061424d7F27b642174c95de7c34093b23 +REMOTE_SPL = 0xc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49 +USER_SPL_ATA = 0x586931089de14bbc55cc4ee29c5d4d9be9db9967ab6770c0fda2337c09091749 LOCAL_ERC20 = 0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9 -REMOTE_ERC20 = 0xd3a7769953f7fb046eb4798298fcc95464633ac2431115abd9466b2e5e595637 -USER_REMOTE_ERC20_ATA = 0x9012c89076034a0fa661a446930bbbbea61967cc0aecf53f7ef1f979b6981d22 +REMOTE_ERC20 = 0x65f82c36d6f5234552a6178fca402e2ba55d659fb04c6ab1f05f9e182a08b309 +USER_REMOTE_ERC20_ATA = 0x659abca3e23fa5a8212846977d3f8d2593b4b0c565afcc38aa89ea6d184cfb06 LOCAL_ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE -REMOTE_ETH = 0xfd77b603f3afb749a8f12944db51cdef33ed6c23b5c7b96a6f5b4c0ac5cd37a6 -USER_REMOTE_ETH_ATA = 0xbdcf27b8fab01afd8e6eae1784baeaf99cb6f3059b42c33e83ec5409ac5fc307 +REMOTE_ETH = 0x643e7e02722311e58bfbd87573bdde4231289334ed5749a2e67ecf9bd8cd3dba +USER_REMOTE_ETH_ATA = 0xf86b5aa217cca19080d4c9fb685bc671bc882e49a1764e0ecfbfaabc360bb661 SOLANA_SOL_RECEIVER = 0x82c9f09a109bce580bb82c13c310689fd00e2225f8dd22015271620ecc035221 -#################################################################################################### +# #################################################################################################### .PHONY: deps deps: clean-lib forge-deps @@ -62,7 +62,7 @@ coverage: @ forge coverage --no-match-coverage "(script|test)" .PHONY: dev-deploy -dev-deploy: deploy create-wrapped-sol create-token +dev-deploy: deploy create-wrapped-sol create-wrapped-spl .PHONY: deploy deploy: @@ -72,9 +72,9 @@ deploy: create-wrapped-sol: TOKEN_NAME=WrappedSOL TOKEN_SYMBOL=wSOL REMOTE_TOKEN=$(REMOTE_SOL) forge script CreateTokenScript --account testnet-admin --rpc-url $(BASE_RPC) --broadcast -vvvv -.PHONY: create-token -create-token: - TOKEN_NAME=WrappedSPL REMOTE_TOKEN=$(REMOTE_SPL) forge script CreateTokenScript --account testnet-admin --rpc-url $(BASE_RPC) --broadcast -vvvv +.PHONY: create-wrapped-spl +create-wrapped-spl: + TOKEN_NAME=WrappedSPL TOKEN_SYMBOL=wSPL REMOTE_TOKEN=$(REMOTE_SPL) forge script CreateTokenScript --account testnet-admin --rpc-url $(BASE_RPC) --broadcast -vvvv .PHONY: create-mock-token create-mock-token: diff --git a/base/broadcast/CreateToken.s.sol/84532/run-1752076599.json b/base/broadcast/CreateToken.s.sol/84532/run-1752076599.json new file mode 100644 index 00000000..8a239c8f --- /dev/null +++ b/base/broadcast/CreateToken.s.sol/84532/run-1752076599.json @@ -0,0 +1,93 @@ +{ + "transactions": [ + { + "hash": "0x5ba3a1619ff0c73d9f5a9d948d62cde8a069e658872536df839064185c3796e7", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "function": "deploy(bytes32,string,string,uint8)", + "arguments": [ + "0x069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000", + "WrappedSOL", + "wSOL", + "9" + ], + "transaction": { + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "gas": "0x52fcd", + "value": "0x0", + "input": "0x23c3601f069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a57726170706564534f4c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000477534f4c00000000000000000000000000000000000000000000000000000000", + "nonce": "0x1d3", + "chainId": "0x14a34" + }, + "additionalContracts": [ + { + "transactionType": "CREATE2", + "address": "0x4d3210a178de60668986eecff4ec0b2508eee1b2", + "initCode": "0x60523d8160223d397350512eb50026bb7e04f877b2e4ce8667e4b2e11760195155f3363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3" + } + ], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x63709e", + "logs": [ + { + "address": "0x4d3210a178de60668986eecff4ec0b2508eee1b2", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0xb9871b8e8afd722754f66be1ff17c4e19a7381e07b703d304694fd9ac8d4bef1", + "blockNumber": "0x1ad992b", + "transactionHash": "0x5ba3a1619ff0c73d9f5a9d948d62cde8a069e658872536df839064185c3796e7", + "transactionIndex": "0x14", + "logIndex": "0xb5", + "removed": false + }, + { + "address": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "topics": [ + "0x0b84965add45c4d10c5aacc22714edc5f88def8df83d2c1f9d18b45ef2d28783", + "0x0000000000000000000000004d3210a178de60668986eecff4ec0b2508eee1b2", + "0x069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000" + ], + "data": "0x00000000000000000000000025f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "blockHash": "0xb9871b8e8afd722754f66be1ff17c4e19a7381e07b703d304694fd9ac8d4bef1", + "blockNumber": "0x1ad992b", + "transactionHash": "0x5ba3a1619ff0c73d9f5a9d948d62cde8a069e658872536df839064185c3796e7", + "transactionIndex": "0x14", + "logIndex": "0xb6", + "removed": false + } + ], + "logsBloom": "0x00000000800000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000001000000000000000000000000000000000000000000000000000000000000800000000000400000000000000000000000000000000000000000000000000000000000000008000100800000000000000080000000080000000000000000000000000000000000000000000000000080000000000000000001000000000000000000000000000000000000000000002000000000004000000000000080000000000000000000000000000000000000008000800000000000000000000000000", + "type": "0x2", + "transactionHash": "0x5ba3a1619ff0c73d9f5a9d948d62cde8a069e658872536df839064185c3796e7", + "transactionIndex": "0x14", + "blockHash": "0xb9871b8e8afd722754f66be1ff17c4e19a7381e07b703d304694fd9ac8d4bef1", + "blockNumber": "0x1ad992b", + "gasUsed": "0x3c14f", + "effectiveGasPrice": "0xf4283", + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "contractAddress": null, + "l1BaseFeeScalar": "0x44d", + "l1BlobBaseFee": "0x79a12e17", + "l1BlobBaseFeeScalar": "0xa118b", + "l1Fee": "0x25cb15ff61", + "l1GasPrice": "0xb5a7d3", + "l1GasUsed": "0x788" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1752076599, + "chain": 84532, + "commit": "116c10e" +} \ No newline at end of file diff --git a/base/broadcast/CreateToken.s.sol/84532/run-1752077024.json b/base/broadcast/CreateToken.s.sol/84532/run-1752077024.json new file mode 100644 index 00000000..c173a3df --- /dev/null +++ b/base/broadcast/CreateToken.s.sol/84532/run-1752077024.json @@ -0,0 +1,93 @@ +{ + "transactions": [ + { + "hash": "0x77b27f4405a1d04baa3a6742f1c0ae3adc3ae250ae2eec14a1c916393bb4a567", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "function": "deploy(bytes32,string,string,uint8)", + "arguments": [ + "0x6ccf56ff18093bc61462cd67c8aa86216fbad049e41d9bffa0856e5f34af5498", + "WrappedSPL", + "wSPL", + "9" + ], + "transaction": { + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "gas": "0x57e41", + "value": "0x0", + "input": "0x23c3601f6ccf56ff18093bc61462cd67c8aa86216fbad049e41d9bffa0856e5f34af5498000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a5772617070656453504c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047753504c00000000000000000000000000000000000000000000000000000000", + "nonce": "0x1d4", + "chainId": "0x14a34" + }, + "additionalContracts": [ + { + "transactionType": "CREATE2", + "address": "0xbc4027074e544be820b1a16bf4f4f7c626d61032", + "initCode": "0x60523d8160223d397350512eb50026bb7e04f877b2e4ce8667e4b2e11760195155f3363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3" + } + ], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x1c5112", + "logs": [ + { + "address": "0xbc4027074e544be820b1a16bf4f4f7c626d61032", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0xe8cad95d7a672993776554ca82b2d195f66ba12f2627c95db3617c6a66ee231b", + "blockNumber": "0x1ad9a00", + "transactionHash": "0x77b27f4405a1d04baa3a6742f1c0ae3adc3ae250ae2eec14a1c916393bb4a567", + "transactionIndex": "0x8", + "logIndex": "0x47", + "removed": false + }, + { + "address": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "topics": [ + "0x0b84965add45c4d10c5aacc22714edc5f88def8df83d2c1f9d18b45ef2d28783", + "0x000000000000000000000000bc4027074e544be820b1a16bf4f4f7c626d61032", + "0x6ccf56ff18093bc61462cd67c8aa86216fbad049e41d9bffa0856e5f34af5498" + ], + "data": "0x00000000000000000000000025f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "blockHash": "0xe8cad95d7a672993776554ca82b2d195f66ba12f2627c95db3617c6a66ee231b", + "blockNumber": "0x1ad9a00", + "transactionHash": "0x77b27f4405a1d04baa3a6742f1c0ae3adc3ae250ae2eec14a1c916393bb4a567", + "transactionIndex": "0x8", + "logIndex": "0x48", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000200000800000000000000000000000000000000000000000000000000000000000000000000000000010000000000001000000000000000000000000000000000000000000000000000000010000000000000000400000000000000000000000000000000000000000000000000000000000000000000100800000000000000080000000280000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000001000000000000000004000000000000080000000000000000000000000000000000000000001004000000000000000000000040", + "type": "0x2", + "transactionHash": "0x77b27f4405a1d04baa3a6742f1c0ae3adc3ae250ae2eec14a1c916393bb4a567", + "transactionIndex": "0x8", + "blockHash": "0xe8cad95d7a672993776554ca82b2d195f66ba12f2627c95db3617c6a66ee231b", + "blockNumber": "0x1ad9a00", + "gasUsed": "0x3c18b", + "effectiveGasPrice": "0xf4283", + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "contractAddress": null, + "l1BaseFeeScalar": "0x44d", + "l1BlobBaseFee": "0x40e5ade2", + "l1BlobBaseFeeScalar": "0xa118b", + "l1Fee": "0x1592e0a489", + "l1GasPrice": "0x1680eba", + "l1GasUsed": "0x80e" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1752077024, + "chain": 84532, + "commit": "116c10e" +} \ No newline at end of file diff --git a/base/broadcast/CreateToken.s.sol/84532/run-1752077085.json b/base/broadcast/CreateToken.s.sol/84532/run-1752077085.json new file mode 100644 index 00000000..edca64e6 --- /dev/null +++ b/base/broadcast/CreateToken.s.sol/84532/run-1752077085.json @@ -0,0 +1,93 @@ +{ + "transactions": [ + { + "hash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "function": "deploy(bytes32,string,string,uint8)", + "arguments": [ + "0xc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49", + "WrappedSPL", + "wSPL", + "9" + ], + "transaction": { + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "gas": "0x57e41", + "value": "0x0", + "input": "0x23c3601fc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a5772617070656453504c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047753504c00000000000000000000000000000000000000000000000000000000", + "nonce": "0x1d5", + "chainId": "0x14a34" + }, + "additionalContracts": [ + { + "transactionType": "CREATE2", + "address": "0xe545c49061424d7f27b642174c95de7c34093b23", + "initCode": "0x60523d8160223d397350512eb50026bb7e04f877b2e4ce8667e4b2e11760195155f3363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3" + } + ], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x171bdb", + "logs": [ + { + "address": "0xe545c49061424d7f27b642174c95de7c34093b23", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0x6baec0f1ce0825ab1d8f722a8b8a06eada7985c0463c9382922a262ffac8bd88", + "blockNumber": "0x1ad9a1e", + "transactionHash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionIndex": "0x5", + "logIndex": "0x32", + "removed": false + }, + { + "address": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "topics": [ + "0x0b84965add45c4d10c5aacc22714edc5f88def8df83d2c1f9d18b45ef2d28783", + "0x000000000000000000000000e545c49061424d7f27b642174c95de7c34093b23", + "0xc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49" + ], + "data": "0x00000000000000000000000025f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "blockHash": "0x6baec0f1ce0825ab1d8f722a8b8a06eada7985c0463c9382922a262ffac8bd88", + "blockNumber": "0x1ad9a1e", + "transactionHash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionIndex": "0x5", + "logIndex": "0x33", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000100000000000000000000000000000000000000100800000000000000080000000080000000000000040000000000000400000000000004000020000000100000000000000000000000000000000000000000000000000000000000000000000004000000000000080000000000000000000008000000000000000000000000000000000800000000000000", + "type": "0x2", + "transactionHash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionIndex": "0x5", + "blockHash": "0x6baec0f1ce0825ab1d8f722a8b8a06eada7985c0463c9382922a262ffac8bd88", + "blockNumber": "0x1ad9a1e", + "gasUsed": "0x3c18b", + "effectiveGasPrice": "0xf4282", + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "contractAddress": null, + "l1BaseFeeScalar": "0x44d", + "l1BlobBaseFee": "0x34a2f9df", + "l1BlobBaseFeeScalar": "0xa118b", + "l1Fee": "0x1180cabfa1", + "l1GasPrice": "0x1b7f67c", + "l1GasUsed": "0x80e" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1752077085, + "chain": 84532, + "commit": "116c10e" +} \ No newline at end of file diff --git a/base/broadcast/CreateToken.s.sol/84532/run-latest.json b/base/broadcast/CreateToken.s.sol/84532/run-latest.json index ce3bafc6..edca64e6 100644 --- a/base/broadcast/CreateToken.s.sol/84532/run-latest.json +++ b/base/broadcast/CreateToken.s.sol/84532/run-latest.json @@ -1,31 +1,31 @@ { "transactions": [ { - "hash": "0xbec860a5b045d2b5e194e8947db19405d9470ad39de0abd4443aeaa0b1566313", + "hash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", "transactionType": "CALL", "contractName": null, - "contractAddress": "0xe52e3b464ba5fe19bdbf3609eb039f2032c457f5", + "contractAddress": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", "function": "deploy(bytes32,string,string,uint8)", "arguments": [ - "0x069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000", - "WrappedSOL", - "wSOL", + "0xc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49", + "WrappedSPL", + "wSPL", "9" ], "transaction": { - "from": "0x8c1a617bdb47342f9c17ac8750e0b070c372c721", - "to": "0xe52e3b464ba5fe19bdbf3609eb039f2032c457f5", - "gas": "0x52fcd", + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", + "gas": "0x57e41", "value": "0x0", - "input": "0x23c3601f069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a57726170706564534f4c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000477534f4c00000000000000000000000000000000000000000000000000000000", - "nonce": "0x474", + "input": "0x23c3601fc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a5772617070656453504c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047753504c00000000000000000000000000000000000000000000000000000000", + "nonce": "0x1d5", "chainId": "0x14a34" }, "additionalContracts": [ { "transactionType": "CREATE2", - "address": "0x314752245b830f3fef1be33eaf16ff510ba769a4", - "initCode": "0x60523d8160223d39736872ea689d101efa7eea27f25c2c220190721e3360195155f3363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3" + "address": "0xe545c49061424d7f27b642174c95de7c34093b23", + "initCode": "0x60523d8160223d397350512eb50026bb7e04f877b2e4ce8667e4b2e11760195155f3363d3d373d3d363d602036600436635c60da1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3" } ], "isFixedGasLimit": false @@ -34,60 +34,60 @@ "receipts": [ { "status": "0x1", - "cumulativeGasUsed": "0x33b814", + "cumulativeGasUsed": "0x171bdb", "logs": [ { - "address": "0x314752245b830f3fef1be33eaf16ff510ba769a4", + "address": "0xe545c49061424d7f27b642174c95de7c34093b23", "topics": [ "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000001", - "blockHash": "0x40f1e5d5db47805bf57146082772ea0089a0de1f6ce9abb55cb20069e335a930", - "blockNumber": "0x1a92222", - "transactionHash": "0xbec860a5b045d2b5e194e8947db19405d9470ad39de0abd4443aeaa0b1566313", - "transactionIndex": "0x9", - "logIndex": "0x96", + "blockHash": "0x6baec0f1ce0825ab1d8f722a8b8a06eada7985c0463c9382922a262ffac8bd88", + "blockNumber": "0x1ad9a1e", + "transactionHash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionIndex": "0x5", + "logIndex": "0x32", "removed": false }, { - "address": "0xe52e3b464ba5fe19bdbf3609eb039f2032c457f5", + "address": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", "topics": [ "0x0b84965add45c4d10c5aacc22714edc5f88def8df83d2c1f9d18b45ef2d28783", - "0x000000000000000000000000314752245b830f3fef1be33eaf16ff510ba769a4", - "0x069be72ab836d4eacc02525b7350a78a395da2f1253a40ebafd6630000000000" + "0x000000000000000000000000e545c49061424d7f27b642174c95de7c34093b23", + "0xc146c29c76c66edd4414993e6d4dae459fbd892d078b41a6bfde7b44261e0c49" ], - "data": "0x0000000000000000000000008c1a617bdb47342f9c17ac8750e0b070c372c721", - "blockHash": "0x40f1e5d5db47805bf57146082772ea0089a0de1f6ce9abb55cb20069e335a930", - "blockNumber": "0x1a92222", - "transactionHash": "0xbec860a5b045d2b5e194e8947db19405d9470ad39de0abd4443aeaa0b1566313", - "transactionIndex": "0x9", - "logIndex": "0x97", + "data": "0x00000000000000000000000025f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "blockHash": "0x6baec0f1ce0825ab1d8f722a8b8a06eada7985c0463c9382922a262ffac8bd88", + "blockNumber": "0x1ad9a1e", + "transactionHash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionIndex": "0x5", + "logIndex": "0x33", "removed": false } ], - "logsBloom": "0x00000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000001000000000000000000000000000000000000000000004000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000100800000000000000080800000080000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000004000000000000000000000008000000000000000020000000000000020800000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000100000000000000000000000000000000000000100800000000000000080000000080000000000000040000000000000400000000000004000020000000100000000000000000000000000000000000000000000000000000000000000000000004000000000000080000000000000000000008000000000000000000000000000000000800000000000000", "type": "0x2", - "transactionHash": "0xbec860a5b045d2b5e194e8947db19405d9470ad39de0abd4443aeaa0b1566313", - "transactionIndex": "0x9", - "blockHash": "0x40f1e5d5db47805bf57146082772ea0089a0de1f6ce9abb55cb20069e335a930", - "blockNumber": "0x1a92222", - "gasUsed": "0x3c14f", - "effectiveGasPrice": "0xf427f", - "from": "0x8c1a617bdb47342f9c17ac8750e0b070c372c721", - "to": "0xe52e3b464ba5fe19bdbf3609eb039f2032c457f5", + "transactionHash": "0x2301e732becea6a80bc2f13f4d91f099c907e2d8999c8408ee8efb28bc450bf5", + "transactionIndex": "0x5", + "blockHash": "0x6baec0f1ce0825ab1d8f722a8b8a06eada7985c0463c9382922a262ffac8bd88", + "blockNumber": "0x1ad9a1e", + "gasUsed": "0x3c18b", + "effectiveGasPrice": "0xf4282", + "from": "0x25f7fd8f50d522b266764cd3b230edaa8cbb9f75", + "to": "0x5262f53ced23a198963dd6b3625c01e5a98266ed", "contractAddress": null, "l1BaseFeeScalar": "0x44d", - "l1BlobBaseFee": "0x1", + "l1BlobBaseFee": "0x34a2f9df", "l1BlobBaseFeeScalar": "0xa118b", - "l1Fee": "0x5ceb115", - "l1GasPrice": "0x2bc2eba", - "l1GasUsed": "0x788" + "l1Fee": "0x1180cabfa1", + "l1GasPrice": "0x1b7f67c", + "l1GasUsed": "0x80e" } ], "libraries": [], "pending": [], "returns": {}, - "timestamp": 1751491364, + "timestamp": 1752077085, "chain": 84532, - "commit": "7ff6f77" + "commit": "116c10e" } \ No newline at end of file diff --git a/base/deployments/base_sepolia.json b/base/deployments/base_sepolia.json index eddb0eb8..49ab4286 100644 --- a/base/deployments/base_sepolia.json +++ b/base/deployments/base_sepolia.json @@ -2,6 +2,6 @@ "Bridge": "0x96BB7fE0B5927CD604B1CfcaD4E16bB82bd1cc11", "CrossChainERC20Factory": "0x5262f53ceD23a198963Dd6B3625c01E5a98266Ed", "Twin": "0x97BaAb1980ED6fe7E963139A465eC117752d9B0e", - "WrappedSOL": "0x10B1A14E40a951777f4180875B2aBe03Ff5DEbae", - "WrappedSPL": "0x26703A110c7F7834B8de61657182bF03b5c5b5a0" -} + "WrappedSOL": "0x4D3210A178De60668986eecfF4eC0B2508eEE1B2", + "WrappedSPL": "0xBc4027074e544Be820b1a16Bf4F4f7c626D61032" +} \ No newline at end of file diff --git a/base/deployments/base_sepolia_prod.json b/base/deployments/base_sepolia_prod.json index b9425178..3f49620e 100644 --- a/base/deployments/base_sepolia_prod.json +++ b/base/deployments/base_sepolia_prod.json @@ -2,6 +2,6 @@ "Bridge": "0xfcde89DFe9276Ec059d68e43759a226f0961426F", "CrossChainERC20Factory": "0xE52E3b464Ba5fe19bdbF3609Eb039F2032C457f5", "Twin": "0xd71C27B3e4D1A4bd3aDFc2397446719651441b4a", - "WrappedSOL": "0x314752245b830F3FEF1BE33Eaf16fF510Ba769a4", - "WrappedSPL": "0x124229C60213709087c408ffe33D2b1142F91125" -} + "WrappedSOL": "0xe545c49061424d7F27b642174c95de7c34093b23", + "WrappedSPL": "0xe545c49061424d7F27b642174c95de7c34093b23" +} \ No newline at end of file diff --git a/base/script/actions/CreateToken.s.sol b/base/script/actions/CreateToken.s.sol index e31aa81a..d65b3669 100644 --- a/base/script/actions/CreateToken.s.sol +++ b/base/script/actions/CreateToken.s.sol @@ -36,8 +36,12 @@ contract CreateTokenScript is Script { function run() public { vm.startBroadcast(); - address token = - crossChainERC20Factory.deploy({remoteToken: REMOTE_TOKEN, name: tokenName, symbol: tokenSymbol, decimals: 9}); + address token = crossChainERC20Factory.deploy({ + remoteToken: REMOTE_TOKEN, + name: tokenName, + symbol: tokenSymbol, + decimals: 9 + }); console.log("Deployed Token at: %s", token); vm.stopBroadcast(); diff --git a/base/test/mocks/Counter.sol b/base/test/mocks/Counter.sol index 1b524a82..1baa482d 100644 --- a/base/test/mocks/Counter.sol +++ b/base/test/mocks/Counter.sol @@ -7,4 +7,8 @@ contract Counter { function increment() external { count++; } + + function incrementPayable() external payable { + count++; + } } diff --git a/base/test/mocks/CounterValue.sol b/base/test/mocks/CounterValue.sol deleted file mode 100644 index f3261050..00000000 --- a/base/test/mocks/CounterValue.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.28; - -import {Ownable} from "solady/auth/Ownable.sol"; - -contract CounterValue is Ownable { - uint256 public count; - - constructor() { - _initializeOwner(msg.sender); - } - - function increment() external payable { - count++; - } - - function withdraw() external onlyOwner { - (bool success,) = msg.sender.call{value: address(this).balance}(""); - require(success, "Call failed"); - } -} diff --git a/solana/Anchor.toml b/solana/Anchor.toml index 082653b7..4025c9e6 100644 --- a/solana/Anchor.toml +++ b/solana/Anchor.toml @@ -1,31 +1,9 @@ -[toolchain] -anchor_version = "0.31.0" +# This file is now only used to build the IDL via `bun generate:idl`. [features] resolution = false skip-lint = false -[programs.devnet] -bridge = "4L8cUU2DXTzEaa5C8MWLTyEV8dpmpDbCjg8DNgUuGedc" - -[registry] -url = "https://api.apr.dev" - [provider] -cluster = "devnet" -wallet = "~/.config/solana/deployer.devnet.alpha.json" - -[scripts] -bridge-call = "bun run scripts/solana-to-base/bridge-call.ts" -bridge-call-value = "bun run scripts/solana-to-base/bridge-call-value.ts" -bridge-sol = "bun run scripts/solana-to-base/bridge-sol.ts" -bridge-spl = "bun run scripts/solana-to-base/bridge-spl.ts" -bridge-wrapped-token = "bun run scripts/solana-to-base/bridge-wrapped-token.ts" -create-mint = "bun run scripts/spl/create-mint.ts" -create-user-ata = "bun run scripts/spl/create-user-ata.ts" -initialize = "bun run scripts/initialize.ts" -mint-spl = "bun run scripts/spl/mint-spl.ts" -prove-message = "bun run scripts/base-to-solana/prove-message.ts" -pubkey-to-bytes32 = "bun run scripts/utils/pubkey-to-bytes32.ts" -relay-message = "bun run scripts/base-to-solana/relay-message.ts" -wrap-token = "bun run scripts/solana-to-base/wrap-token.ts" +cluster = "localnet" +wallet = "~/.config/solana/id.json" diff --git a/solana/Cargo.toml b/solana/Cargo.toml index 92e201f9..68da9ddd 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -6,6 +6,7 @@ resolver = "2" overflow-checks = true lto = "fat" codegen-units = 1 + [profile.release.build-override] opt-level = 3 incremental = false diff --git a/solana/bun.lock b/solana/bun.lock index 4f1bbf7c..355dd6e3 100644 --- a/solana/bun.lock +++ b/solana/bun.lock @@ -3,14 +3,17 @@ "workspaces": { "": { "name": "solana", - "dependencies": { - "@coral-xyz/anchor": "^0.31.0", - "@solana/spl-token": "^0.4.13", - "@solana/web3.js": "^1.98.2", - "viem": "^2.30.0", - }, "devDependencies": { + "@codama/nodes-from-anchor": "^1.2.1", + "@codama/renderers-js": "^1.2.14", + "@solana-program/system": "^0.7.0", + "@solana-program/token": "^0.5.1", + "@solana-program/token-2022": "^0.4.2", + "@solana/kit": "^2.1.1", + "@solana/spl-token": "^0.4.13", "@types/bun": "latest", + "codama": "^1.3.0", + "viem": "^2.30.0", }, "peerDependencies": { "typescript": "^5", @@ -22,11 +25,23 @@ "@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="], - "@coral-xyz/anchor": ["@coral-xyz/anchor@0.31.1", "", { "dependencies": { "@coral-xyz/anchor-errors": "^0.31.1", "@coral-xyz/borsh": "^0.31.1", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.69.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA=="], + "@codama/errors": ["@codama/errors@1.3.0", "", { "dependencies": { "@codama/node-types": "1.3.0", "chalk": "^5.4.1", "commander": "^13.1.0" }, "bin": { "errors": "bin/cli.cjs" } }, "sha512-pYp/gOi1c/9btrybWvH2iK5jj7SOpMmyLm9EqbxrivV+lqzfEogU8IG+BLch9sqnuhPgg/PzCaB4sYd438UC/Q=="], + + "@codama/node-types": ["@codama/node-types@1.3.0", "", {}, "sha512-3dPbMXR/QgKUqXMMxc6PUi2f8Dq6eZ9P/v6+ChTQFLGhSQmqC1PsCbL77PuM7mf4IK9np1RXFqbC/EVpgLvbgA=="], + + "@codama/nodes": ["@codama/nodes@1.3.0", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/node-types": "1.3.0" } }, "sha512-Spf+Whm4jBLFxGPtJuDGmteGe+avoIDnh6rsByU1iJlYmcJJLjZayexFkvW8+1IeDclUMPQYBSj6SjuQEItLqQ=="], + + "@codama/nodes-from-anchor": ["@codama/nodes-from-anchor@1.2.1", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "@codama/visitors": "1.3.0", "@noble/hashes": "^1.8.0", "@solana/codecs": "2.1.1" } }, "sha512-IzQtPDeKt4LyBWCspFtArEtyYWSthl8my9UzGHqBgvnXx/glyiJ3YuyMV7UbT7uId0Sk2iNW79N9ARp3myVKjg=="], + + "@codama/renderers-core": ["@codama/renderers-core@1.0.16", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "@codama/visitors-core": "1.3.0" } }, "sha512-IJshH6JsX7GUaYmC6KlOd5pLLyW1Yqd0I8B0pVWvvv9BfPNosC4t4RfusHSkYQePkIvs7CJ7YlwUywwW36Vt8A=="], - "@coral-xyz/anchor-errors": ["@coral-xyz/anchor-errors@0.31.1", "", {}, "sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ=="], + "@codama/renderers-js": ["@codama/renderers-js@1.2.14", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "@codama/renderers-core": "1.0.16", "@codama/visitors-core": "1.3.0", "@solana/codecs-strings": "^2.1.0", "nunjucks": "^3.2.4", "prettier": "^3.5.3" } }, "sha512-KukxwlX5iJpMBMRsiVY6vrmMipw8MbgHdNO3WFkNV1hO2F/ykhZfdHtZVEVL0Z/dNCBNfExLpign00teneCIoA=="], - "@coral-xyz/borsh": ["@coral-xyz/borsh@0.31.1", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.69.0" } }, "sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw=="], + "@codama/validators": ["@codama/validators@1.3.0", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "@codama/visitors-core": "1.3.0" } }, "sha512-s6rG3fJSvS7zc+fQ5yJN1NHlGQiut+P3Bpi5sQcEikjdWDDezu4OOgkxTh8ksTsCu8Rsif2hVzFYDLMcKjNrag=="], + + "@codama/visitors": ["@codama/visitors@1.3.0", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "@codama/visitors-core": "1.3.0" } }, "sha512-xcFae6NwC6Omr9tDm6P8rF3IDm5jWe2VMPomixaAc7+mpRhAWLbsg+FYCi0E09NvADo4C1tGo8U/SsvZLATUsQ=="], + + "@codama/visitors-core": ["@codama/visitors-core@1.3.0", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "json-stable-stringify": "^1.3.0" } }, "sha512-Lldy0aOc882QYDa1IhjXhwpDsQE7oirBaebRddggXYFQs4+cvFROibHXBqG2npHPvQM4Mot6dJHQqffB/QL4iQ=="], "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], @@ -40,23 +55,77 @@ "@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], + "@solana-program/system": ["@solana-program/system@0.7.0", "", { "peerDependencies": { "@solana/kit": "^2.1.0" } }, "sha512-FKTBsKHpvHHNc1ATRm7SlC5nF/VdJtOSjldhcyfMN9R7xo712Mo2jHIzvBgn8zQO5Kg0DcWuKB7268Kv1ocicw=="], + + "@solana-program/token": ["@solana-program/token@0.5.1", "", { "peerDependencies": { "@solana/kit": "^2.1.0" } }, "sha512-bJvynW5q9SFuVOZ5vqGVkmaPGA0MCC+m9jgJj1nk5m20I389/ms69ASnhWGoOPNcie7S9OwBX0gTj2fiyWpfag=="], + + "@solana-program/token-2022": ["@solana-program/token-2022@0.4.2", "", { "peerDependencies": { "@solana/kit": "^2.1.0", "@solana/sysvars": "^2.1.0" } }, "sha512-zIpR5t4s9qEU3hZKupzIBxJ6nUV5/UVyIT400tu9vT1HMs5JHxaTTsb5GUhYjiiTvNwU0MQavbwc4Dl29L0Xvw=="], + + "@solana/accounts": ["@solana/accounts@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/rpc-spec": "2.2.1", "@solana/rpc-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ER0WFzCKB7FSbcY0rZqVaqi8HmkVBCu/k92R5hvVfIrxykElGiRGzXRV/4A8HZUfZGA3Vif+6p3DJ9jumHVGdw=="], + + "@solana/addresses": ["@solana/addresses@2.2.1", "", { "dependencies": { "@solana/assertions": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/nominal-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-PpaGtoghW2z1YJ2yyHm9OPKxmEVnLHuZWRxV/iKgslsMigy3ZuTV8HbmrzVP3idPzVjvcl/pmOQqpwq8Wm8Dkw=="], + + "@solana/assertions": ["@solana/assertions@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-yJ3o/fva0iBbqEsmiXh0WsmNACyJpupT8VOf7TTXwqwDF40fMJE0aU+szVcpuLxf9MTmGZuFIZF1F/NWdAvZ5A=="], + "@solana/buffer-layout": ["@solana/buffer-layout@4.0.1", "", { "dependencies": { "buffer": "~6.0.3" } }, "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA=="], "@solana/buffer-layout-utils": ["@solana/buffer-layout-utils@0.2.0", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/web3.js": "^1.32.0", "bigint-buffer": "^1.1.5", "bignumber.js": "^9.0.1" } }, "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g=="], - "@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], + "@solana/codecs": ["@solana/codecs@2.1.1", "", { "dependencies": { "@solana/codecs-core": "2.1.1", "@solana/codecs-data-structures": "2.1.1", "@solana/codecs-numbers": "2.1.1", "@solana/codecs-strings": "2.1.1", "@solana/options": "2.1.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-89Fv22fZ5dNiXjOKh6I3U1D/lVO/dF/cPHexdiqjS5k5R5uKeK3506rhcnc4ciawQAoOkDwHzW+HitUumF2PJg=="], "@solana/codecs-core": ["@solana/codecs-core@2.1.1", "", { "dependencies": { "@solana/errors": "2.1.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-iPQW3UZ2Vi7QFBo2r9tw0NubtH8EdrhhmZulx6lC8V5a+qjaxovtM/q/UW2BTNpqqHLfO0tIcLyBLrNH4HTWPg=="], - "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.1.1", "", { "dependencies": { "@solana/codecs-core": "2.1.1", "@solana/codecs-numbers": "2.1.1", "@solana/errors": "2.1.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-OcR7FIhWDFqg6gEslbs2GVKeDstGcSDpkZo9SeV4bm2RLd1EZfxGhWW+yHZfHqOZiIkw9w+aY45bFgKrsLQmFw=="], "@solana/codecs-numbers": ["@solana/codecs-numbers@2.1.1", "", { "dependencies": { "@solana/codecs-core": "2.1.1", "@solana/errors": "2.1.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-m20IUPJhPUmPkHSlZ2iMAjJ7PaYUvlMtFhCQYzm9BEBSI6OCvXTG3GAPpAnSGRBfg5y+QNqqmKn4QHU3B6zzCQ=="], - "@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + "@solana/codecs-strings": ["@solana/codecs-strings@2.1.1", "", { "dependencies": { "@solana/codecs-core": "2.1.1", "@solana/codecs-numbers": "2.1.1", "@solana/errors": "2.1.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-uhj+A7eT6IJn4nuoX8jDdvZa7pjyZyN+k64EZ8+aUtJGt5Ft4NjRM8Jl5LljwYBWKQCgouVuigZHtTO2yAWExA=="], + + "@solana/errors": ["@solana/errors@2.2.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-BiCivvqhNsg5BiWTshsRwGC/866ycfAxj/KMV+uH9pKohXyEENXedgj6U3fAIJiJLdSFya61CLl2EnDygnUPBg=="], + + "@solana/fast-stable-stringify": ["@solana/fast-stable-stringify@2.2.1", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-AQclweD4HYuYUJawle+Ut3heZRd8gifcKCIWc4bi9joNDGrwRFfmZXN/VJwBjqMfto/gf2dw13HoUIOk1va/Sw=="], + + "@solana/functional": ["@solana/functional@2.2.1", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-GafhYZMx/6oWSkNxzLYTVAElbkx3quX3hERvp4Gxic/RjInVWt0H7jlFevNqnB6aa/vh6Q26Y4n6gAmizN8f5A=="], + + "@solana/instructions": ["@solana/instructions@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-Zy6+mqr76kTUHMemMoyiHh35k8PNLo7XqWIpmUFXWwB742g8pwR198/3mMz5H55/wp1SuFU3MpmWe6A7YozRaQ=="], + + "@solana/keys": ["@solana/keys@2.2.1", "", { "dependencies": { "@solana/assertions": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/nominal-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-AaYdtiRcnptkdWO4rvz0LG0SKhriYU9bsGA7kAZk7Hhxhf9pGTib/TnT8YMW3xlUVCblaorN7QQwyzhXObG0qA=="], + + "@solana/kit": ["@solana/kit@2.2.1", "", { "dependencies": { "@solana/accounts": "2.2.1", "@solana/addresses": "2.2.1", "@solana/codecs": "2.2.1", "@solana/errors": "2.2.1", "@solana/functional": "2.2.1", "@solana/instructions": "2.2.1", "@solana/keys": "2.2.1", "@solana/programs": "2.2.1", "@solana/rpc": "2.2.1", "@solana/rpc-parsed-types": "2.2.1", "@solana/rpc-spec-types": "2.2.1", "@solana/rpc-subscriptions": "2.2.1", "@solana/rpc-types": "2.2.1", "@solana/signers": "2.2.1", "@solana/sysvars": "2.2.1", "@solana/transaction-confirmation": "2.2.1", "@solana/transaction-messages": "2.2.1", "@solana/transactions": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-JnWdsm2aPvNiwY3cWe7EjEhwFBnpk/BA5UZtTEgN1ej+sJg/5XjtU/+5gUi8NSeYDX7UMRbnKbiplHZOy0DiDw=="], + + "@solana/nominal-types": ["@solana/nominal-types@2.2.1", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-/9HTEbFjp2Ty3dPaboONIiRUr2fkVtAI2iAIfoVzkUZvkDwRvJK7fqQfOQOpS8Q8491o5rqu867guDJrXy60AA=="], + + "@solana/options": ["@solana/options@2.1.1", "", { "dependencies": { "@solana/codecs-core": "2.1.1", "@solana/codecs-data-structures": "2.1.1", "@solana/codecs-numbers": "2.1.1", "@solana/codecs-strings": "2.1.1", "@solana/errors": "2.1.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-rnEExUGVOAV79kiFUEl/51gmSbBYxlcuw2VPnbAV/q53mIHoTgCwDD576N9A8wFftxaJHQFBdNuKiRrnU/fFHA=="], + + "@solana/programs": ["@solana/programs@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-CP0WQTgrwsghajMJ70kwI/KR5vmNCtfrN2JKLEzHEms3x2Fd8Kg0Ccablgeo6RdASBr8o1CulCg7UHO38XXbMA=="], + + "@solana/promises": ["@solana/promises@2.2.1", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-CBHz9YxQjNsOL8XA+m16O4m51O7niKBKdjv2JXdFEr5yhleCzuCClf8IKytB0hYQbHPh7k0QFflBBNK74VxByw=="], + + "@solana/rpc": ["@solana/rpc@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/fast-stable-stringify": "2.2.1", "@solana/functional": "2.2.1", "@solana/rpc-api": "2.2.1", "@solana/rpc-spec": "2.2.1", "@solana/rpc-spec-types": "2.2.1", "@solana/rpc-transformers": "2.2.1", "@solana/rpc-transport-http": "2.2.1", "@solana/rpc-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-1+vxbE8Abahx1SHiOA5ztzBTDwbrCGj6TzFur41Oyx56wjdeWM28O4YZy1y0bSkwIfm7sgU4uBomZwWzKMzYLQ=="], + + "@solana/rpc-api": ["@solana/rpc-api@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/keys": "2.2.1", "@solana/rpc-parsed-types": "2.2.1", "@solana/rpc-spec": "2.2.1", "@solana/rpc-transformers": "2.2.1", "@solana/rpc-types": "2.2.1", "@solana/transaction-messages": "2.2.1", "@solana/transactions": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-5gagodQi29JLWUnPZusDMqYWX7AG9ElYvgscWqfeqm/aQP4JMvkdhVkqYRM5vfRuKtyXtmipka8nD+4XyVGfKA=="], + + "@solana/rpc-parsed-types": ["@solana/rpc-parsed-types@2.2.1", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-4SR5GZOnqviuvb9qco8AZvHSN45FRTzcnNsqXIKJsy4kfuoqc0n0LnzjElvny0DlE3QrVwjBAko7O893m1aiWQ=="], + + "@solana/rpc-spec": ["@solana/rpc-spec@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/rpc-spec-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-1/OpJYIKOg/vqm2ZOJmR139X/J91bkDtFhAC3hvcOhfK2oBVmW+E2p0ifHFPBhmTiPXEwdFd29sT3i3baun5Hw=="], + + "@solana/rpc-spec-types": ["@solana/rpc-spec-types@2.2.1", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-5e1fO7s0XYR4t0CFguxwOPx/1OUmO1MdIYA4U/jl5UISweExh1ZmF58nXI+P21GiAuzZ5mSDpnVvEZA73zGF9g=="], + + "@solana/rpc-subscriptions": ["@solana/rpc-subscriptions@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/fast-stable-stringify": "2.2.1", "@solana/functional": "2.2.1", "@solana/promises": "2.2.1", "@solana/rpc-spec-types": "2.2.1", "@solana/rpc-subscriptions-api": "2.2.1", "@solana/rpc-subscriptions-channel-websocket": "2.2.1", "@solana/rpc-subscriptions-spec": "2.2.1", "@solana/rpc-transformers": "2.2.1", "@solana/rpc-types": "2.2.1", "@solana/subscribable": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-9i1G7Ul5jFCR+imsSWvnw47rgGtYmsFh41ugKF77oWTdUnGuLpAIU1gnhFGkL8mFv4P1Zo0mCIdilfIFuqgM1Q=="], - "@solana/errors": ["@solana/errors@2.1.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw=="], + "@solana/rpc-subscriptions-api": ["@solana/rpc-subscriptions-api@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/keys": "2.2.1", "@solana/rpc-subscriptions-spec": "2.2.1", "@solana/rpc-transformers": "2.2.1", "@solana/rpc-types": "2.2.1", "@solana/transaction-messages": "2.2.1", "@solana/transactions": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-N9aInBLrP3ipIt+MfT+EiaHyGoxOh7xXsXLuqJ8YlnzJ2DcT9IsFnwKysucbIf+ZFK++9tmbFbfWSFQX14Ngog=="], - "@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + "@solana/rpc-subscriptions-channel-websocket": ["@solana/rpc-subscriptions-channel-websocket@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/functional": "2.2.1", "@solana/rpc-subscriptions-spec": "2.2.1", "@solana/subscribable": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3", "ws": "^8.18.0" } }, "sha512-BPp+pkfNOQH1aVtblg6AxZGdGqp+sIqzBIxDy2tRcDOlu+1ogyHPhbbmYCB/Hdy64DDmqYqp1CcXFKpL3y69tQ=="], + + "@solana/rpc-subscriptions-spec": ["@solana/rpc-subscriptions-spec@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/promises": "2.2.1", "@solana/rpc-spec-types": "2.2.1", "@solana/subscribable": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-NEwyHtmwU9WLxDy5cSF8/VgRtWvJM2/tm7PEKQeOCLJkC0weBGt/xrevOgjKJRaVUG8+0iBB4xFtCvWZFKHXAg=="], + + "@solana/rpc-transformers": ["@solana/rpc-transformers@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/functional": "2.2.1", "@solana/nominal-types": "2.2.1", "@solana/rpc-spec-types": "2.2.1", "@solana/rpc-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-X/V1+9zpqOGs4A5eZ3qOhXsToXLIizfoHFV33RXY5ltqDfrGgah1NGOS/sbn/Fv6rqFZyhnnGl+HyIo8yPUO1A=="], + + "@solana/rpc-transport-http": ["@solana/rpc-transport-http@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1", "@solana/rpc-spec": "2.2.1", "@solana/rpc-spec-types": "2.2.1", "undici-types": "^7.9.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-5yZDzGdlmht53h0rov0EqvK6Sh1VM8QJuln7ZuENvbY8UD8PdW37PM+/bvkmhABKxnwocx7BR0jo7dPsA+AV/w=="], + + "@solana/rpc-types": ["@solana/rpc-types@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/nominal-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-JUpSJV5ffWaEMdz7a3Pm8ctNxNHV1tpOu7qz+X3N9wJMAp82zUg4wxHZ0iQjda4sQK2ahRHw7z8PPnsRQsBM/A=="], + + "@solana/signers": ["@solana/signers@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1", "@solana/instructions": "2.2.1", "@solana/keys": "2.2.1", "@solana/nominal-types": "2.2.1", "@solana/transaction-messages": "2.2.1", "@solana/transactions": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-HxNqY1URLjeyDo+NRnULO8IQHx43Bm/Xg1JAN5khpTKfTMkvJzSwAwjAj0LbbVfYR01Q7aRCFsuA2OfbfsPkSg=="], "@solana/spl-token": ["@solana/spl-token@0.4.13", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-group": "^0.0.7", "@solana/spl-token-metadata": "^0.1.6", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.95.5" } }, "sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w=="], @@ -64,24 +133,40 @@ "@solana/spl-token-metadata": ["@solana/spl-token-metadata@0.1.6", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA=="], + "@solana/subscribable": ["@solana/subscribable@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-7uRA8Gs/i2F6Wkq97xQ9IS4HLewn1gLm7siEr/JNMr/FNWjaa9S7Vs3U9/ni9l82JTn7YwpqcWfIZVJgaKQUCA=="], + + "@solana/sysvars": ["@solana/sysvars@2.2.1", "", { "dependencies": { "@solana/accounts": "2.2.1", "@solana/codecs": "2.2.1", "@solana/errors": "2.2.1", "@solana/rpc-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-UG1gKnGxjQcRSR0NIIr7iypgxjo3gfS67OVxig+IKSJ7+SPf315bkQKhzBYjzasUKkTsBFqJeAD97vAPWQ6cKg=="], + + "@solana/transaction-confirmation": ["@solana/transaction-confirmation@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/keys": "2.2.1", "@solana/promises": "2.2.1", "@solana/rpc": "2.2.1", "@solana/rpc-subscriptions": "2.2.1", "@solana/rpc-types": "2.2.1", "@solana/transaction-messages": "2.2.1", "@solana/transactions": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-1Go8ybsYSwqwdE0qX0L9t7xcBAAi0s+qjiZxtabxOI4LPV78+HPyYu5ZxW5Ky69GwRa8d8XjEQPT6Te+sHtMmA=="], + + "@solana/transaction-messages": ["@solana/transaction-messages@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-data-structures": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1", "@solana/functional": "2.2.1", "@solana/instructions": "2.2.1", "@solana/nominal-types": "2.2.1", "@solana/rpc-types": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-C96w1AHxuckYuoNYkLlEn4q6QN/wUlp62v7+W44lge8cbD3PhiM/rbIPJmIyUnCM14/+EXhSMs27434vFyv+CA=="], + + "@solana/transactions": ["@solana/transactions@2.2.1", "", { "dependencies": { "@solana/addresses": "2.2.1", "@solana/codecs-core": "2.2.1", "@solana/codecs-data-structures": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1", "@solana/functional": "2.2.1", "@solana/instructions": "2.2.1", "@solana/keys": "2.2.1", "@solana/nominal-types": "2.2.1", "@solana/rpc-types": "2.2.1", "@solana/transaction-messages": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-cPR3rN2xkhPZIJ70gvhKn5bpW3MpzH77aeO+Hnzrbtk4ogDHSvadqOAHSAfYyTDleCdkHGA0uVfpnxrpPqY3Vw=="], + "@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], - "@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="], + "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - "@types/node": ["@types/node@24.0.7", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw=="], + "@types/node": ["@types/node@24.0.12", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="], + + "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], "@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + "a-sync-waterfall": ["a-sync-waterfall@1.0.1", "", {}, "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA=="], + "abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="], "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], + "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -100,27 +185,41 @@ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - "buffer-layout": ["buffer-layout@1.2.2", "", {}, "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA=="], - "bufferutil": ["bufferutil@4.0.9", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw=="], - "bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="], + "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], "chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "codama": ["codama@1.3.0", "", { "dependencies": { "@codama/errors": "1.3.0", "@codama/nodes": "1.3.0", "@codama/validators": "1.3.0", "@codama/visitors": "1.3.0" } }, "sha512-rqnD0DRq7H3yJce3nDU1Kdy3Y/TsC/GirS4VazG7AEJ5UbR0fGIoThWWOpl2d5J1JmsaheY7APCY4PpnEZBoJA=="], - "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], + "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], "delay": ["delay@5.0.0", "", {}, "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "es6-promise": ["es6-promise@4.2.8", "", {}, "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="], "es6-promisify": ["es6-promisify@5.0.0", "", { "dependencies": { "es6-promise": "^4.0.3" } }, "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ=="], - "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], @@ -130,55 +229,81 @@ "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "isomorphic-ws": ["isomorphic-ws@4.0.1", "", { "peerDependencies": { "ws": "*" } }, "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="], "isows": ["isows@1.0.7", "", { "peerDependencies": { "ws": "*" } }, "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg=="], "jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + "json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="], + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + "nunjucks": ["nunjucks@3.2.4", "", { "dependencies": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", "commander": "^5.1.0" }, "peerDependencies": { "chokidar": "^3.3.0" }, "optionalPeers": ["chokidar"], "bin": { "nunjucks-precompile": "bin/precompile" } }, "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + "ox": ["ox@0.8.1", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.8", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-e+z5epnzV+Zuz91YYujecW8cF01mzmrUtWotJ0oEPym/G82uccs7q0WDHTYL3eiONbTUEvcZrptAKLgTBD3u2A=="], - "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], + "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "rpc-websockets": ["rpc-websockets@9.1.1", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA=="], "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + "stream-chain": ["stream-chain@2.2.5", "", {}, "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="], "stream-json": ["stream-json@1.9.1", "", { "dependencies": { "stream-chain": "^2.2.5" } }, "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw=="], - "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], + "superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], - "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + "undici-types": ["undici-types@7.11.0", "", {}, "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA=="], "utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="], "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], - "viem": ["viem@2.31.6", "", { "dependencies": { "@noble/curves": "1.9.2", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.1", "ws": "8.18.2" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-2PPbXL/8bHQxUzaLFDk4R6WHifTcXxAqMC8/j6RBgXl/OexQ1HU8r9OukwfDTqrFoHtWWiYz5fktHATy7+U9nQ=="], + "viem": ["viem@2.31.7", "", { "dependencies": { "@noble/curves": "1.9.2", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.1", "ws": "8.18.2" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-mpB8Hp6xK77E/b/yJmpAIQcxcOfpbrwWNItjnXaIA8lxZYt4JS433Pge2gg6Hp3PwyFtaUMh01j5L8EXnLTjQQ=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -186,54 +311,172 @@ "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], - "@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/accounts/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/accounts/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], + + "@solana/addresses/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/addresses/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], + + "@solana/codecs-core/@solana/errors": ["@solana/errors@2.1.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw=="], + + "@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.1.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw=="], + + "@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.1.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw=="], + + "@solana/codecs-strings/@solana/errors": ["@solana/errors@2.1.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw=="], + + "@solana/instructions/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/keys/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/keys/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], + + "@solana/kit/@solana/codecs": ["@solana/codecs@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-data-structures": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/options": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-IOm/ZjfZb7dJQ+9IeFeqZSWbE5YPTICj8noqQxE6Dvx3obDxZXf55qaa3CD8ShsUjwmuPo/LLQBlCwXkQE9qiQ=="], - "@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + "@solana/options/@solana/errors": ["@solana/errors@2.1.1", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw=="], - "@solana/codecs-data-structures/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/rpc-api/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], - "@solana/codecs-data-structures/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + "@solana/rpc-api/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], - "@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + "@solana/rpc-types/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], - "@solana/codecs-strings/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/rpc-types/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], - "@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + "@solana/rpc-types/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], - "@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + "@solana/signers/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], - "@solana/errors/commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], + "@solana/spl-token-group/@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], - "@solana/options/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/spl-token-metadata/@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], - "@solana/options/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + "@solana/sysvars/@solana/codecs": ["@solana/codecs@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-data-structures": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/options": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-IOm/ZjfZb7dJQ+9IeFeqZSWbE5YPTICj8noqQxE6Dvx3obDxZXf55qaa3CD8ShsUjwmuPo/LLQBlCwXkQE9qiQ=="], - "@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + "@solana/transaction-confirmation/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], - "@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + "@solana/transaction-messages/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/transaction-messages/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-HsvFs+yZ5+tceBPgHpUvgFuAJHa/yjXhsxFMeVJDuK6PzA/CrNZw49xooarHFr52QjsNMdLCY4Vb0DfC+IRKrA=="], + + "@solana/transaction-messages/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/transactions/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/transactions/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-HsvFs+yZ5+tceBPgHpUvgFuAJHa/yjXhsxFMeVJDuK6PzA/CrNZw49xooarHFr52QjsNMdLCY4Vb0DfC+IRKrA=="], + + "@solana/transactions/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/transactions/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], + + "@solana/web3.js/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + "jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - "ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + "nunjucks/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], "rpc-websockets/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + "@solana/accounts/@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/addresses/@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/keys/@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/kit/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/kit/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-HsvFs+yZ5+tceBPgHpUvgFuAJHa/yjXhsxFMeVJDuK6PzA/CrNZw49xooarHFr52QjsNMdLCY4Vb0DfC+IRKrA=="], + + "@solana/kit/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/kit/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], + + "@solana/kit/@solana/codecs/@solana/options": ["@solana/options@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-data-structures": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-wytheS66Ge/7jC2O+oT30U1L0PeIWlgqjTEOdFA3NfBhWo3ZfUKNLoUQeOP9USByJ3kUTDD1yti+qrp67GiItA=="], + + "@solana/rpc-api/@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + + "@solana/spl-token-group/@solana/codecs/@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-HsvFs+yZ5+tceBPgHpUvgFuAJHa/yjXhsxFMeVJDuK6PzA/CrNZw49xooarHFr52QjsNMdLCY4Vb0DfC+IRKrA=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/sysvars/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-iC/j//whtHg8+fXkwEJcIx+9uc6amGl7QwP2j9mt+bn2OLZimjzzoOXFtahspRPLDyiDb0g3UKyWGHnRKxRtjQ=="], + + "@solana/sysvars/@solana/codecs/@solana/options": ["@solana/options@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/codecs-data-structures": "2.2.1", "@solana/codecs-numbers": "2.2.1", "@solana/codecs-strings": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-wytheS66Ge/7jC2O+oT30U1L0PeIWlgqjTEOdFA3NfBhWo3ZfUKNLoUQeOP9USByJ3kUTDD1yti+qrp67GiItA=="], + + "@solana/transaction-confirmation/@solana/codecs-strings/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/transaction-confirmation/@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.2.1", "", { "dependencies": { "@solana/codecs-core": "2.2.1", "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qlJHWZFGzhMa7R6EZXNM/ycINGrR4lzBQjwFMs2pXnCxqKTI3Vru0f4kSh0qqf6U1bjNLaYXTMniqETX6ANpzg=="], + + "@solana/web3.js/@solana/codecs-numbers/@solana/codecs-core": ["@solana/codecs-core@2.2.1", "", { "dependencies": { "@solana/errors": "2.2.1" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZW1kTmvqhQhk/jMDo7wZgApn1Lf+d3AecHF6bcWPVSr+KlGLtWZL0wcP+0tnsncPhvG28pZxRR57f4TUylSA7Q=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/spl-token-group/@solana/codecs/@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/codecs/@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/codecs/@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], } } diff --git a/solana/bun.lockb b/solana/bun.lockb deleted file mode 100755 index c24333d0d04b2b458c69fec4910f7020478cec7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37570 zcmeHw2|QKZ_xHsuX(EXxjfSLa9+Np`s6-MPWVohqaW5qmDwQS;l+vJ7G?AhK(WFt5 zq(UjpN~22iyVkjPb2Omm`TgJj?|nb*Ppf^;+57w5d#}CraQ3;^8az-nlpCVz?i;Ae z4h$RM9vavYgyqlinBnE?&t`dXLIT`)tT45X!VCr@f4AC!7g)GC^?VM~n_X#8HB4k7aFT!0eLLoeh;`-6U6SJUS8}F2E&)l zX6tHeF&f(Q^A8f>s{w;Ny+H*SUl){fL6l!l9@pR39q#4BHP$y9Vl3}8{D;@=p;FkM z&`@7b$i|Qo@Lq%am>wX|Q+vVv&m#Q$uOY_vAQ-+GyL4bxtzczJv)OE zk?#t`Sl%dzQ4jhM!@nqYIGZYInBZC#l!y5}+5T?fz(@4-WBq$Ter&fpCxq(*`3^|% zdEL3%dRPy4H!hn6eH_+}&*vVf;)eyvgTSpQHdML~#5E9OJ-|RceFMF@zEC-!iYkZu zSYI26dqeCh#V_v~#Qor!^zZNb^*H21yUKLPon33XCpET?t>89fw9cu5zLlb% z=I=l5S~u5X>&|&kZdux$3zNPveCV)wMMjCC7pFAHrp@Z;;Jnv=jWRQJ`pJI6&WA6u zhg-zO?;PIut!j~Lw_x?&hquZ{4O5O`yVuO#v&7x}!O)IYA8z(4c2OwrsvjuX!^Y~$ z{b;o@(o=3eeV*~4puWsGYV-U-yi&8Lg;pb%51D;3ZsEkxZ}$C1HcYbL#Q65|)5XF^ za$2HK)b^KXOF1}K)r$@;UwU%ejyW@Zxar?NiSPBjbqLiWraevp}rVr#MxTAjk$eZjIk<_?{`UUV_fN+4b}={7|V~( z4XuB$BkRod>>F{G(%%MI6l7dmx!!D1J6UyU!&UEhCg;eC%&$6qb*QdSeiy^K`gy1HKf8=H zUT5q7`RLNOlUxo+e!8%r=E>J~Y{$$j?GBfMRgc`O_dHZora|=dWBqr#*u{2xxHtIS zrsYHIJT&xoM6_S2@ob>V<8h(KU44}-2lwggw&f*z>cC3XX^PVZKJHhSUwUZNm5FMj zJJ$={FU~tBR-dM%zbwwlw>nJN&1B#DJxS-eVf7uN!k(2)vex+$K0U|U(#X?s%&;R_ z>GMBrjUHG~Rkjq^{ISfx$zOP!)OE1n>jjL$1QHRW=N$$ zW#y8$PfC^QL^exzNYZqXs()l{uXLqzQR1YF?Ujz(s72RDJomo5MDp1nn7;p2z*Pu? z1&BI~BfRbp0@H7s0>P(2ZWiEig@Pf^Aop7%A^GYc<1oNue&lFty9nMI0Am1;IG9h{ z90-0L01X9rnDW~k2>u%2%>WO}92Kx0Z3zV513nlC%E$J%H4yxG8Xo4uKi2;;!0QXj zZ)@LU9ZC6D;DfFJkN4WjLGbNC>7;yEy4xHG-Wc$(AXEL1;I`_J;G+PqLBqGT4g`M? z@WukXJCslEx7HAnF9`M>Xuo)kw)3a<^AzwS0FU;EZHMZ#DUkBz0XXVklph9oEdQU@ zKNs+5|FG7#x$W3MQvdgW$Nqze0jg*-2f+^kgS7{ITlJ1*V<7n1fVT!bmW`pUJOqCR z@LGU}hzft&e?nle#Qu@(8v!2mhWGwde|=%og8LtW{nPPp33x0Y z^#{*+ZEgdp{~`gNT$326S_~xL8NieIzpZ_b#01~5bJP0OVu3#-61)@Oas4EE`&0ez z06f|cp(i}#`gbuY{~q9R{Gk1id%wfb?~}Zqu<+sj8Fh#A2bfcv0>Q5aJh49*ihpc> zm4G*a@{tdP%){`p#X!pM1BVmi08em#s()X=n*kp6hv850djU`S53y~XLrDEA1?7|a zQe2;Kzns6VVXmaotzbkkUy2YA#U!rEGQ%uC8Y4S2L)Vt3>| z=4mOAeBEHdR|7oO9RuOvU;X}nf;R=cHsDFW!F1ZDK=9FkNByDw;=MmDzX0&W{t!=i zTdM@g_Zsl#P(J2I9l&(bratg8&o|#tc6dl z=+6)E|EB#_fQQbZ>=(<1%Qgj4{%AO)$MH+93AnBOzZCE|e-d0<=U`IaO~9K7@Ob{% zRvi+&8$5X6{QD?lGfwofl;mn*0^cd?s3Iy|wrc(JaUc(wm z-M13lr^lFXEl7vCoC@?9)5m~dJeErRb&Tz>1;H5RR4UM8oTI0LV7e;^@=XW9FrE6J zALD&D!8OKs?Jl_fRgCE#f^>}W+7m_j+r2cn+t-tXb>!SAqb`~5?seZi~(cZBdx>>2pA(i9t88R1HnC93J8XlVyxc| zDp&Iu%TE>D#~Aaa31YmB<)Mgy{Iz?&e(3fNYQa4XrMJI!kJ|YE!S26_f5UOUV}7CIv1TKZbi z)8qCOCPz5BC}=+X$m+{{^YWqLW*RTf2_!H(U#tnXI?yYjN5;!23DLtj@?+|(yVjrR z;d$c4wFC+MogD^S`F_?sG%Qv&tkt z^h!@M8FI8c)9}Sj8ZXX)Brv}RogT5RHf?F9o!Qg3gByzOmU%ekSN6H?a_NgmL2xf` zGX^Iw^Z39~vfb8tYUm{>ZqADv-`*)?RiH)PV;hep_B3Alb0zcUiUFHqY_mTWoxZW< zz3f!Ez`E4S^IkprpttUW#@Ejo{cP^rR+g&VUa6Ll=aI1ciiyNRjRNKDW&3t3T@8BO ze}FlS7w16|n7w?3^7YnuW*A81+|(Z^X}36G;1#dDgVo}f7{!+V#M2_rsw)LJT!UXIp=Fe#Mmo`A{}B&9b=|u2Y0wj9Fh^GB_DM&6)Z<0XG3WZ{CQtS6H#{C+UwbmFZo;AZHMa5dV>ir_ zlce#|&t;f{CzZ}o+n~C(zF_Wi$JDi5hE6$HGT=+~0h!gM*XBOjE|M^4i_oX&Q!f_G zKhUM(O#73To)=zsO7g#zI;zEN(G~TYHvJf-d?+lXQtn~>v*wJK+zu~DG8Q+{s zc6VI@$2-{C`>kd#k{=N_dO=Ll=0&<)MF^X<|P(W&UyA*{bQ{JH$$RNw_Z9 zly6e8CvkU!NvN*qzPDmDUO4V+49wI??vr=as+QKj;07P>-uXrM&ZigaPpLI7SGaU9 zxkGQys3Vo~W5!Ne(ACoM7KHAhwJi`~ZF{^z!ujxL@2oi!!>@;HU$1Nz%OiW@}8 ziF8)AsW#rF<3E>^Ix=$Kr)TC}2W>DH_pRy&A{ za2(Van8od!b5^ViPV7J4YTwq>m3Pd#MD)lTDWta{ZPjZL|5wVsBjgThC=45)HB+{| zT+*BoQc=2ANA{jSHO6|N;aer8 zoAc%LPFGzB?=R+fZMQ+UN389EaW=2>{FckNJ81lBrM+py<8Kjq`&RqWc1b9W65XeBl)6g)6H8^qrYje1KJ8pF@b#(8Src^TSb3i5 z8792fzpVSI>f_J)(sv(yx$mgS!CB{n6VF)K3vaF3^KDh(YW=y7 zU#S-vdi%(*bPsoZcCkjYp_8SKL1sk}M|I$~S0e7u_UO@g;kcnOFhh&>EHt=LrZPMs zY>H#@ZsmIc>QX&;3%WdeRsG07r+8WX!EL#N_qIE$Y}x6>#p(NxD_t)hHZ$;pM#BN; zClkeLeQ3P!7~U9|j6E*yc8}lOk(_vFaVOQJ$2Cjm*U!q&6?yai<^H3q6p{?)Bwt&j zFOs2>vdp?nK`m>o&gEHeUR~*(w`{1-eU~jxG+y%jisa0)n6sMo)rLl?a~E`&W;V4r za@pP0!!{gmr#j)VUeAjDW3N{$$9E}>$+26eW|t$GJ@3uBaM|%$>y%(UvOE8BAHBZ# z+)Dz}J5{qN=V)hj=B)_%0z^Xp~HMHkmp^;wcMpg1>Q zV(=RcyU=3`PhF1MORH}`I`4z|nO|4T9CcRq)7f;x%#x?8CM;Y>64;)MuSvytn@Z|b8+``ihUBA%!4bM_YU?#5){19EjsYwWX z5O0$IYRiT_n?Ei)aMyTKhuNHMqSqJ;llL1Zepo*tRrf^l^|xHP;rFhLjIf`wJ^n+f z& zzW()v>y3+-ocpL&;eG3P#F~euC)bq7KV;~hxn9oNI+iCV%!ak=sS`M&!K&0}TE zGqr71PE-%I(68$dlrYEOt^N(W#yg14 zJ3U&;K3^m9V&1lnGDh(`x4c>5kX#b~Ryy5d(}Ll$IV&IJb%~d;J$Yj59kp!}VhudC zjzf(I1sQ+NzK7c6+l{tiWNnS6J4SZrS3UI(l8a!^%E+j>aoT=WQq-c1v{mWYtAZ z6BZ^_d2iizs_SO?I)?cR-#JCu3W_ghny{@%Ee}D7m%nHx=n;LV)V=g9dvF+h5JMwIfW!8%8 z9~u&C85oQT(aZK^Tjcbvg%Yf&MMq3wWCXNBJQ$QxdWc^QuSq?b$?CcRig7Ab6wj*dg`=`>&G15 zJ?eAH$S1eiUAqa7cd%JCVR32uq$k&Qi`BX5OeD8M7n~)oNV1d3ofD6pgx9G+t#oZ{{}r7ZJP9j?1VD(|!Ept!Vq+r3cqs zvbg#xKGXBd0G0eVhs=bEyJalt5;@j1R>d=6&lM$3iDF*Vddd5XRW{zVrST4<^WI#g zSZ&N0z30C2nO$#|UhQNV8E_%8>-Nvt-Ea5W(xGg~ZJB$$)s7{e>Goz?&9b*~(~g}k zT=dH4%%%PbZZnpLdmRmk{VtO{7nhnD1mCKKw8toBG z>&~Yixc&N>VhF?ib@HU1G+sQfCV}axH2q2Y#N)d&rkjqqb^H8<+b^vK4K}Zpov*X@ za-WrVitCaUEtHC$aD1|ghD6BO%DK#qxcEvs#v(X>+X1`X{v&C;nuHW$W_t;au7}W0 z&kD<#jur)#C(Es_du@s7vaD?P-Bn8cS*9_i)1`$B{Y^*q4zgNRs{KGlysAk1iZ*lE zqq>5jxtcdn_sS`bafo17#u5?8>Fr9 zK0oi~ejTwZv*Ry6jyT8$;uT zcOQ*`*>~01bL(9SYg7wMg7X44ac=5m*}NJww&v~Ad#+;^?T_6sLFYl3xP-rMM$Fxl zZ$x?DKgY!_*6!A6<7@Hdj&k+iX}tJ8fdr<-J)xmy{Uerdyq_m;xcK}07w(*W@qKJh zL>P*h#%q3v9pq9r{`eY+0KF}XF1+gZrqpReNm!3*yO(WwzBrV(r+zPuSC5cF%Vtuxaw=5d@Y00+; z)rWFc_q@A)Jok1veO&0%dF9(zCP`0VKbgw)*;nMg?@`ndE$92;`*JsyNbj$VNUJn) z*gxRLt#si{CoU~|^e*qv%PN;!8;ZYlkM86-XQYbFX3*Y5y49pG(_IbJsb#T6N*xWnzVZn?d zh5dIQ&9(8E;CuZ1?6-y?^4d8GhfE~)E9|!n%;1$v?Y=SM&Mk>$-iznB&N-G>GK|Jc z-V-4?^L3t{dYt2mN|9SBECcgHdJ4KbgA;XSH{YCe&SGN6H3Q(x%qD z&biZAr}D~-PSaPvI>;@I?>$+V#tZL08v}Fr*z(YC9KBf6bVkOwjM-7W#7@^Zbk{Xq zpm1o$>PuN4b1$erSkX7HVbkUF!4b@`EdT2(O_t6~TGy^LyfCPyqbiNpxRDj=d(P;U zd69WcRosg*^^=v;Sc_xks^n!4?jpAJW_5P#y`!V%q&!VClTTcp9xhaCFjRcuxJ+&9 zl*_%p?BCx{=DJ-Tjn{!eNBP0Txd_tE4bp+i*zo>_mrt5ef{_0`Amvo1w5PS3C{EbP?&VAQ@W zUG|yzsVj#&>MMb9M6kHj>VJ_{hzW zl~LQa?j0(bdZx?3=((ao*|y)VXuW-{(nayG+=#MGLnF_0l3zA&T>8i1b=KFq6{!?% zO!U;>D&jdwq4UH%+H;l#oj295;K;{q`S(-4j$W29?bx$z3%+$qJLOWpaE@903hDQO zMb5FE%BBufiOUjt?qAP~>sS_Q#nT#NSfsY+20N`XmR8?Ubl$5;^A3f4p7?e0vUVFP z%cDlgJE``oQS&`x?;2b^vr@F-!;1dexo;1%a+Wsq%I=`GqVvuG#rK0xuDoV<$9{E9 z3Eh52(|IL|E7Tlr1SSQ1Z$DLIJg1_%gX1s>)hDwpGoKvX+B0&rpWKFcH?o%h9QR(Y<%cdf3bh0^OBWZqAIZ?JuvWhbZd zZgv$ig{L}h?crbQan-P=L7_vr)L=Vatnj9!ZI5QXGc-N8daAn65}F>Y=)8M-cdMI{ zq?MRCX|qj9fStJ46Sl5;QakBQ$s6TXtDA3Jq1^td&4Am^udU`3TP^fo8d37taKZ-B zn$gbTPXku@?WghLdu0-s=}M;xM$QnjpS#cY*@mRLqh`}(azRaE|oal5?+ zal6oX@qHf&%z(X_Y3E80ofH)}v+ky_BC&EszRfx7`!fTwqh#Ve^k2QW`8oN_?3Wgg z9=Yw!uztGX_L*w)=nL}m;`(g7{C4x63p8Hx-W<7^vo$&9eqt{lRY|p%jte-pUe2@M ztjwdLNp(`f%X;NMa%v-Wlbkx<2Ne-F`P3?>JIf zXVf?!}!Jaj*AYx#j+~J^ROUqBU|p zIagm@_oQzBt*#|k=eUbTMu$ZYKG9`;)R*BAKC9=wAF!wDXbp{bJe}9DK=%H*{N!bc zch4?=7Pa~L!|eHY6weHN{_?}r5$_`hUVgryD4}O@@dw6B@7#S)6?@y)ay4sR+s6iF zFBx05+;lLFmwe9v>6wYyeMjHga3}TLIuFH>qb;RAd<|RM_e-zYwWXzc4>M24?tI+6 zd&Lrqt=WtA%-@vzPIR@g**=wgKR4O5!5sVT1~8d5&S&=Y`c9e<89VP(MrQr7edTtB z>1zY8Y3%k3ub3p;dyi1|xQb4X6y@r>+w4j_$(Sg*-g}&LjQDGrNOP8=Y3gm6@a%>3 zb$$Y!*VKAJr;n}a{9&E`l ztSit--LUTJ>}$+xhb)dQ8JXmIfL31zI`5^A>%^tQOFWO=vCulFi>4w>`xb)Ike!=t-* ztT44wOFVdn8{RSV%a?Ttp)x1yZjUufdTF1&M2D{5Np#*foffxSbzo?)o7>fAmC7m> zZpyYDe014grkGt_mld?j`;Ldt{bS~{vUhaOG`nc$qOBbD(#th#m*|@mWzn-AGwA2` zPIO-L=M&{}+UwVJp1L7ve7dN+ZO*$hqEB)+kG2T>7VmW{*5RAP_4*-eN`>RqcP2jk zx+2{4YbWRHw{yG457m-BbNxC^56*Pn-u^k#((+;}U+7sm+bO(GVGDPT9CE_S@I#th zV2*#(j2Uu=bQO=DJ?m=8%~CsMG$`+$zL1yW$a9;zT#Zq6&U?U_F8&hh&- zYuBFH>;7ubiLdKIn1i=TY?oQrU}ErKtub>Ww&wIlX_k z2IE>?uRG5yt$Zfil^xn%?Kyhj`u?ty4;6dR>N|zbtJ8htniuV=N0eFinc-oqC!-^s z6lYZb?2)bAy{#QT_2!mjur?pCUi|H5!EHTUaGlZ(vVqeMTYFSU&hUg;{5 zy=70=rw&rR){C4Ky5HMq{T8uzYsP*WyL*?+Lz|1AjH} zR|9`F@K*zWHSkvhe>Lz|1AjH}R|9`F@K*zWHSk}efi%Ipo-g>@bqF0taN)0X_y%%$ zZvOr%Bogh+Ozv$#@V9`2G*`;rjvdT~2(*hWYR} ziZekl;QKM;$KMyu0zr=HAeaw-8yF6P_uN469=2sR2nKf$#Nu!AB0w;(LGT{dZ6yen zSS{piP;8n1e`zpbeqT=z(DW z$APQ@SqKshg8jM_WElwd=RA-|kj1}{4(@Oh!ah%a+*1EaKo+!R)DyNH?F#!FbuJ2m z`bOOoouiEkg9wHs6=535M{qcP&`!`^FcWg2U>}k^_?hq^rx6JD0rn;88*N}H2#&opVQ6N@=duab?hc+N+m)0Pt7qoYB zpMFg~V~n~R2SV=A^N)x7bPhTv%fA2KE<4{4791kq&jY6DQYo7t z6;p^u2u#tTQiu-};u8W>)Kt(yiB}ZjC4!%Vc1!%P5PuZHLV%$Dv>NfyLOdx53&DMC zC>2nnMAuS6Muyr@k{07Sq+ts21|cOhO$!ph@T(yO`Z$XCjUX5e#?Q3|jZ)QK1(@bK zfD}pzKkEQeC?))?YwRDa!Oyye6iV5|#|QCwAS{Hf0SJS5qaofosNU8@WorphV>F8R z@1RoD(RMKWtQ}CpFKR$~`{%mN6C@EoH^lFUun?qZsDN$%+)_x<#|r!twqMs8`*b#k zKO9*)_n(a(8F^AlC{%=+|D%4i5DB57RO?~KBk=x0ykUTL1a?6DzYu>IkfIIk0c8^p zGQ^_>q^R+GoA}@%K1qaypsQ7IrX}AvAYMPHa@3KH_{SmsLLfzh&qh4F5RVsBiW=JW z&l&{_(ZZCUHA`IbX7FKei}Xb5YHx5-7tYMh{+!d#Fq-BK)+!*#Oo08 z!a`vvQzU+gh@Tcn!BGNi!~+iT2m&dfPfQ^`j)>0}VIfRFyC&Xpi1(1DlppOtf*RYJ zpzYtY{T@TSDiJR;P&aMRC{%&?6C(aqusxdmY6Bg8{4~|zXFHfDFa+Y2iFm1@v;%Z7 z^9lSm5kEFm3T1CUS}&yNsZjkxzK=tE(@-gz*grqlV49!?#4i%@6GLG%5JNma5l=V* zHrOl>A5p|-4y33-4b;^ey$&_aNT4^$4t|_rnT;ds#~Bt<5Cb(JzNv_>9wWMEa;@b!2DqTK?c(WqjeSi&TG3X!S--`JAp~|7` z8mE4=1LEliQm{Q>*X%$K4mycixFW&fZp`Xqu*!gLFok%(BHnOf)s2iEa}8=7x7~RDS{c6c=jTmjs$F=YvKcp_(Xyf!P@wHi~q$a(M20o z`=hm$Z`L2MgWs%KkV1T# z5g$*K-l&m9yqghkPmqFE1!Io*J0t#|AVn~Oe?N!(X7+~~5N~C~dlRssUBd>B_(dar zqNo(u3&ALnh_;4=by0s9`nmx z0JZ-7VzyKlY+iuwr(FOR@qbNk#A6`wphj60jBSnoa1H*mR`pLt3GrP>e7V7Bq@IJ- z8N|CG@%Gk~^2;4Dwd4A6G*V+*2Oms+IrqXo;J0%x%sVDM`OnV8?;OQO?W>3b%VmcE zy;hwHk%9VCMT9)6w**Lc?Q*#6=t#|z>PS5*yRd;4;EA>k^4K>>bT6;6n^D*C@wWl<3ej)(Bng6BQk zB#`6o&sOya1yykc7d~!WAGqMcO*Z)L4bs%0eb#TGh?!g{U?!KvKdwP~{+1(@=j$KJ z^J3|-!r+CACme#cK*E^?B%E0+>NE&B*kSBI9y`F72Zo_Z>gyZi!w%t^xOs-Sd3l9{ zXJIZY5FH8o!>gSZ$bSuh0-F)AV1t3rxKMaYTmsecs{$Mz{7-5XJj z8@5&$nspX1@Oupkhm)nYLHV7I6{mV1pl~+W8nnqO^Z|+1p$|ZO7KH{AI|0|bEh$dG z0lAO@{q*AGLD^#)U}%tnqBQ^r$8xRIS#Zt=7{Mn%p>;upr)dr_GK2?gm~mSunL5M- zI2BW1-BybHVORoIFhBu~XSN6r@)|_28MfyScOdRYO7pdA>`)dF+qB#RhBe)Z%`kh}F{_nPafWsWd=1y?ze_s`t8rwj~ZxnFxKQz&JZ~_9{0zKIwEs!+k zq3R;Y&*C3AwbCGJ^NKKZ5DqGU)2mhxzwCWr_~m`dj$0c#M5)b;3>ceTv#96)R_ew_ zG(oA;E5zXV*U@6N*cF1aG(ZbJLFLi-TDFKrW-OJI0!TPSXa(7PqyfD74Hg{h^Em`d z^-Qkd10H9XaA$g``mp_ROt)Ye%@;%{qxlW~8bI|AHBoqn2eI5kef>R~U$HnrJYNp% zmq-kofFE+;D<=xc!cQ%45}HGyQq6BbPwRmlwxV`wLc%p4_1<_vS&4otSi0^2EzlzD z1)$Gbn{%myLjX}Rj8fQzv{o8_qlKLYIE-NHqSwb1djTtGl6 zSOZ58)xJ=0Lhs?rqAJz`2R$2BorYpjC!g4IPuK#&3LQwbtR=D^+TYq9p!uo<#W%kJ z9WB@n_=Ljz2pck1KuZIGmJ7IOS8W>P0l4Q3;8L@4t4l<)#T9UZWfoMXq1)>IgSJWo zly(JW!_#>ywnp_cwb}!?=>o8V4a`g*H{MKdR%nQS3tiQe30RsgsL6twD`0=%>*>$( z-~_^cfeSN3OPc>VGqV1~z{2fpYhwuyzyXaB3|Dx5MSY<0@!yoSJniuF0G$5;EC^K! z1cI>w%?V_C@TTDr4IJ8PvAH^|pb$=gFBi5quqzATgarBc!tRLc$K?bz?R;2VH!qm; zLfCEru)uHvThgrH*@b_`+)8KCD527{D==AH8=&Tbo3MEMwp1)VTN4$90@HxDwo;Q{ zv=`O*vljU4Nh@s4R0%xIuBi#UH8a5X1^~ne%m5!oKo>2jy;)BpklwRRX5DnVrJ0qM zs~HuWGN{(7LcJyd92GYe-9qJmy}9GRb!nyDKdd464yU#C?HB6?3ldK^Gh44o>NuXyZPJQjH&#-cVaEvV`jxquFD(+|yQ!{BHdbJT}I{=p8l&uL& zt<24)VGp+ew!a6Qd3c0yxLp1zV+$3*_eoGTM%3Ea+I&WP_`#+(9be+i1F!R2u`+z+ z1vrdA^{t<@H#-~$V6$s#1JK&uiKYg?)2^Vof|aTT^)zY|u|%^h>YZOJEp65*fNpjT zR*PHwmX|VG_y4#NBNoy^&1q; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const bridge = await program.account.bridge.fetch(bridgePda); - - const [outputRootPda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("outputRootSeed")), - bridge.baseBlockNumber.toBuffer("le", 8), - ], - program.programId - ); - - const outputRoot = await program.account.outputRoot.fetch(outputRootPda); - console.log(`Output Root: ${toHex(Uint8Array.from(outputRoot.root))}`); - - const { event, rawProof, leafIndex, totalLeafCount } = await generateProof( - TRANSACTION_HASH, - bridge.baseBlockNumber - ); - - const proof = { - proof: rawProof.map((e) => [...toBytes(e)]), - leafIndex: new anchor.BN(Number(leafIndex)), - totalLeafCount: new anchor.BN(Number(totalLeafCount)), - }; - - // Ix params - const nonce = new anchor.BN(event.message.nonce); - const sender = [...toBytes(event.message.sender)]; - const data = Buffer.from(toBytes(event.message.data)); - const messageHash = [...toBytes(event.messageHash)]; - - const [messagePda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("incomingMessageSeed")), - toBytes(event.messageHash), - ], - program.programId - ); - - console.log(`Message PDA: ${messagePda.toBase58()}`); - console.log(`Output Root PDA: ${outputRootPda.toBase58()}`); - console.log(`Nonce: ${nonce.toString()}`); - console.log(`Sender: ${event.message.sender}`); - console.log(`Data: ${event.message.data}`); - console.log(`Message Hash: ${event.messageHash}`); - console.log(`Proof: ${JSON.stringify(proof)}`); - - const tx = await program.methods - .proveMessage(nonce, sender, data, proof, messageHash) - .accountsStrict({ - payer: provider.wallet.publicKey, - outputRoot: outputRootPda, - message: messagePda, - systemProgram: SystemProgram.programId, - }) - .rpc({ skipPreflight: true }); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); - -async function generateProof( - transactionHash: Hash, - bridgeBaseBlockNumber: bigint -) { - const publicClient = createPublicClient({ - chain: baseSepolia, - transport: http(), - }); - - const txReceipt = await publicClient.getTransactionReceipt({ - hash: transactionHash, - }); - - // Extract and decode MessageRegistered events - const messageRegisteredEvents = txReceipt.logs - .map((log) => { - try { - const decodedLog = decodeEventLog({ - abi: BRIDGE_ABI, - data: log.data, - topics: log.topics, - }); - - return decodedLog.eventName === "MessageRegistered" - ? { - messageHash: decodedLog.args.messageHash, - mmrRoot: decodedLog.args.mmrRoot, - message: decodedLog.args.message, - } - : null; - } catch (error) { - return null; - } - }) - .filter((event) => event !== null); - - console.log("\n=== MessageRegistered Events ==="); - console.log( - `Found ${messageRegisteredEvents.length} MessageRegistered event(s):` - ); - - if (messageRegisteredEvents.length !== 1) { - throw new Error("Unexpected number of MessageRegistered events detected"); - } - - const event = messageRegisteredEvents[0]!; - - console.log("\n=== MessageRegistered Event ==="); - console.log("Message Hash:", event.messageHash); - console.log("MMR Root:", event.mmrRoot); - console.log("Nonce:", event.message.nonce); - console.log("Sender:", event.message.sender); - console.log("Data:", event.message.data); - - const [rawProof, totalLeafCount] = await publicClient.readContract({ - address: ADDRESSES.bridge, - abi: BRIDGE_ABI, - functionName: "generateProof", - args: [event.message.nonce], - blockNumber: bridgeBaseBlockNumber, - }); - - console.log("\n=== Bridge Contract Proof ==="); - console.log( - `Proof at block ${bridgeBaseBlockNumber.toString()}: ${rawProof} (index: ${ - event.message.nonce - } / total: ${totalLeafCount})` - ); - - return { - event, - rawProof, - leafIndex: event.message.nonce, - totalLeafCount, - }; -} diff --git a/solana/scripts/base-to-solana/relay-message.ts b/solana/scripts/base-to-solana/relay-message.ts deleted file mode 100644 index c319cbde..00000000 --- a/solana/scripts/base-to-solana/relay-message.ts +++ /dev/null @@ -1,272 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { - getMint, - TOKEN_2022_PROGRAM_ID, - TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { PublicKey, SystemProgram } from "@solana/web3.js"; -import { toBytes, toHex } from "viem"; - -import type { Bridge } from "../../target/types/bridge"; -import { getConstantValue } from "../utils/constants"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { deserializeMessage } from "../utils/deserializer"; - -// The message hash from a previously proven message -const MESSAGE_HASH = - "0x5a1e91ae8594a7e58ae2aa213954d7733a5e90b276a37d62800ec00a97e7e66d"; - -const NEW_ACCOUNT_SECRET_KEY = - "0cd60f7db0ca726a07da10e35323042a5b05facc00b781e57b06a59eaf2e2197769b26af0c3e3d129796876e465c21b479aae47bba4e9c964bb556d8d7cf93b2"; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - console.log(`Program ID: ${program.programId.toBase58()}`); - console.log(`Signer: ${provider.wallet.publicKey.toBase58()}`); - - const newAccount = anchor.web3.Keypair.fromSecretKey( - Buffer.from(NEW_ACCOUNT_SECRET_KEY, "hex") - ); - - // Find the message PDA using the message hash (from prove-message) - const [messagePda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("incomingMessageSeed")), - toBytes(MESSAGE_HASH), - ], - program.programId - ); - - // Fetch the message to get the sender for the bridge CPI authority - const message = await program.account.incomingMessage.fetch(messagePda); - - // Find the bridge CPI authority PDA. Not always needed, but simpler to always compute it here. - // It is only really needed if the relayed message needs to CPI into a program that requires - // the bridge CPI authority as a signer. - const [bridgeCpiAuthorityPda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("bridgeCpiAuthoritySeed")), - Buffer.from(message.sender), - ], - program.programId - ); - - console.log(`Message PDA: ${messagePda.toBase58()}`); - console.log(`Bridge CPI Authority PDA: ${bridgeCpiAuthorityPda.toBase58()}`); - console.log(`Message executed: ${message.executed}`); - console.log(`Message sender: ${toHex(Buffer.from(message.sender))}`); - - if (message.executed) { - console.log("Message has already been executed!"); - return; - } - - const messageData = Buffer.from(message.data); - const deserializedMessage = deserializeMessage(messageData); - - const requiredAccounts = { - payer: provider.wallet.publicKey, - message: messagePda, - }; - - let remainingAccounts: { - pubkey: anchor.web3.PublicKey; - isWritable: boolean; - isSigner: boolean; - }[]; - const signers: Array = []; - - if (deserializedMessage.type === "Call") { - console.log( - `Call message with ${deserializedMessage.ixs.length} instructions` - ); - - const { ixs } = deserializedMessage; - - if (ixs.length === 0) { - throw new Error("Zero instructions in call message"); - } - - // Include both the accounts and program IDs for each instruction - remainingAccounts = [ - ...ixs.flatMap((i) => i.accounts), - ...ixs.map((i) => ({ - pubkey: i.programId, - isWritable: false, - isSigner: false, - })), - ]; - signers.push(newAccount); - } else if (deserializedMessage.type === "Transfer") { - console.log( - `Transfer message with ${deserializedMessage.ixs.length} instructions` - ); - - if (deserializedMessage.transfer.type === "Sol") { - console.log("SOL transfer detected"); - const solTransfer = deserializedMessage.transfer; - - console.log(`SOL transfer:`); - console.log(` Remote token: 0x${toHex(solTransfer.remoteToken)}`); - console.log(` To: ${solTransfer.to.toBase58()}`); - console.log(` Amount: ${solTransfer.amount}`); - - const [solVaultPda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("solVaultSeed")), - Buffer.from(solTransfer.remoteToken), - ], - program.programId - ); - - remainingAccounts = [ - { - pubkey: solVaultPda, - isWritable: true, - isSigner: false, - }, - { - pubkey: solTransfer.to, - isWritable: true, - isSigner: false, - }, - { - pubkey: SystemProgram.programId, - isWritable: false, - isSigner: false, - }, - ]; - } else if (deserializedMessage.transfer.type === "Spl") { - console.log("SPL transfer detected"); - const splTransfer = deserializedMessage.transfer; - - console.log(`SPL transfer:`); - console.log(` RemoteToken: 0x${toHex(splTransfer.remoteToken)}`); - console.log(` LocalToken: ${splTransfer.localToken.toBase58()}`); - console.log(` To: ${splTransfer.to.toBase58()}`); - console.log(` Amount: ${splTransfer.amount}`); - - const [tokenVaultPda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("tokenVaultSeed")), - splTransfer.localToken.toBuffer(), - Buffer.from(splTransfer.remoteToken), - ], - program.programId - ); - - const mint = await program.provider.connection.getAccountInfo( - splTransfer.localToken - ); - - if (!mint) { - throw new Error("Mint not found"); - } - - remainingAccounts = [ - { - pubkey: splTransfer.localToken, - isWritable: false, - isSigner: false, - }, - { - pubkey: tokenVaultPda, - isWritable: true, - isSigner: false, - }, - { - pubkey: splTransfer.to, - isWritable: true, - isSigner: false, - }, - { - pubkey: mint.owner, - isWritable: false, - isSigner: false, - }, - ]; - } else if (deserializedMessage.transfer.type === "WrappedToken") { - const wrappedTransfer = deserializedMessage.transfer; - - console.log(`WrappedToken transfer:`); - console.log(` Local Token: ${wrappedTransfer.localToken.toBase58()}`); - console.log(` To: ${wrappedTransfer.to.toBase58()}`); - console.log(` Amount: ${wrappedTransfer.amount}`); - - remainingAccounts = [ - { - pubkey: wrappedTransfer.localToken, - isWritable: true, - isSigner: false, - }, - { - pubkey: wrappedTransfer.to, - isWritable: true, - isSigner: false, - }, - { - pubkey: TOKEN_2022_PROGRAM_ID, - isWritable: false, - isSigner: false, - }, - ]; - // signers.push(newAccount); - } else { - throw new Error("Unexpected transfer type detected"); - } - - // Process the list of optional instructions - const { ixs } = deserializedMessage; - - // Include both the accounts and program IDs for each instruction - remainingAccounts.push( - ...ixs.flatMap((i) => i.accounts), - ...ixs.map((i) => ({ - pubkey: i.programId, - isWritable: false, - isSigner: false, - })) - ); - } else { - throw new Error("Unexpected message type detected"); - } - - // Set the isSigner flag to false for the bridge CPI authority account (if it exists) - remainingAccounts = remainingAccounts.map((acct) => { - if (acct.pubkey.toBase58() === bridgeCpiAuthorityPda.toBase58()) { - return { - ...acct, - isSigner: false, - }; - } - return acct; - }); - - remainingAccounts.forEach((acct, i) => { - console.log(`Account ${i + 1}:`); - console.log(` Pubkey: ${acct.pubkey}`); - console.log(` IsWritable: ${acct.isWritable}`); - console.log(` IsSigner: ${acct.isSigner}`); - }); - - const tx = await program.methods - .relayMessage() - .accountsStrict(requiredAccounts) - .remainingAccounts(remainingAccounts) - .signers(signers) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/constants.ts b/solana/scripts/constants.ts index e8e916ee..5e0b0023 100644 --- a/solana/scripts/constants.ts +++ b/solana/scripts/constants.ts @@ -1,22 +1,65 @@ -// Devnet Dev -// export const CONSTANTS = { -// solanaSpl: "HDfeDzHJaDpW7oVHCysovy64kSdMFFUcsYHxYZjYTi3N", -// wrappedEth: "J4S2C7x3ZnraP46Sav8AQh8LNfM5V9wxbEJFpkiew8Y5", -// wrappedERC20: "FFD8WyHb6RqM5h3L8eiSx6nq2LE4nQoUrnM8ft7ufbHY", -// recipient: "0x8c1a617bdb47342f9c17ac8750e0b070c372c721", -// counterAddress: "0xCdfe10f911eD5039E031D6a7be3a0F97fA061C38", -// counterValue: "0x9a99Fc75F96983dB31523A66D53cDd50eEbfDe26", -// ethAddr: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", -// } as const; +import { address } from "@solana/kit"; +import { fileFromPath } from "./utils/file"; -// Devnet Prod export const CONSTANTS = { - solanaSpl: "HDfeDzHJaDpW7oVHCysovy64kSdMFFUcsYHxYZjYTi3N", - wrappedEth: "Epy2F1JBEj4Rr3zk5iutuKxvrJGxDGbsn1HUxmPnV9ny", - wrappedERC20: "ASJK4fpHvJbadSNcqqq96RR5UNyXmMRj83zK9VsoosX9", - recipient: "0x8c1a617bdb47342f9c17ac8750e0b070c372c721", - counterAddress: "0xCdfe10f911eD5039E031D6a7be3a0F97fA061C38", - counterValue: "0x9a99Fc75F96983dB31523A66D53cDd50eEbfDe26", - ethAddr: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", - erc20Addr: "0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9", + "devnet-alpha": { + // Network + cluster: "devnet", + environment: "alpha", + rpcUrl: "api.devnet.solana.com", + + // Keypairs + deployerKeyPairFile: await fileFromPath( + "keypairs/deployer.devnet.alpha.json" + ), + bridgeKeyPairFile: await fileFromPath("keypairs/bridge.devnet.alpha.json"), + + // Solana addresses + solanaBridge: address("4L8cUU2DXTzEaa5C8MWLTyEV8dpmpDbCjg8DNgUuGedc"), + spl: address("8KkQRERXdASmXqeWw7sPFB56wLxyHMKc9NPDW64EEL31"), + splAta: address("Hw1qKo9UjDxPDUwEFdUvfGs77XFim9CQzvpaMGWRTe7d"), + wEth: address("CURCLcLzb4GFg1o8c841T6yvkrEJXwNarHspTZrk5ZT2"), + wEthAta: address("GMwSrwEcuyGitE8gGrhiHLNL6bnqVpre4ujYozdueqKT"), + wErc20: address("HmQecEWH6q3mxMjVByk1rwPEwPYmeLUhrWAUs8kc4uhV"), + wErc20Ata: address("HCixR6YGDfZY2KLkx8TnMKiXen9oEmr6YmPjHUKpFnY"), + + // Base addresses + baseBridge: "0xfcde89DFe9276Ec059d68e43759a226f0961426F", + eth: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + erc20: "0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9", + wSol: "0x4D3210A178De60668986eecfF4eC0B2508eEE1B2", + wSpl: "0xBc4027074e544Be820b1a16Bf4F4f7c626D61032", + recipient: "0x8c1a617bdb47342f9c17ac8750e0b070c372c721", + counter: "0x5d3eB988Daa06151b68369cf957e917B4371d35d", + }, + "devnet-prod": { + // Network + cluster: "devnet", + environment: "prod", + rpcUrl: "api.devnet.solana.com", + + // Keypairs + deployerKeyPairFile: await fileFromPath( + "keypairs/deployer.devnet.prod.json" + ), + bridgeKeyPairFile: await fileFromPath("keypairs/bridge.devnet.prod.json"), + + // Solana addresses + solanaBridge: address("AvgDrHpWUeV7fpZYVhDQbWrV2sD7zp9zDB7w97CWknKH"), + spl: address("E1UGSzb3zcdQpFsEV4Xc3grxrxMsmHtHdFHuSWC8Hsax"), + splAta: address("6x7ujzdNWDKQPxfW1gosdzegm6sPeNU5BooUfjkQn4Jk"), + wEth: address("7kK3DZWUFHRYUky5aV95CouGYR3XuA3WnEPwQ5s1W8im"), + wEthAta: address("Hij46yANqwuuc2VThykEsHfEH8gvzxPhH9EXspkgL68G"), + wErc20: address("7s3fSFV23MSRssnp7gYam4LEJbBvXTcc6cVXY5duy2Dn"), + wErc20Ata: address("7qd2bgZSkj5hR4yaH3fS9ecx5C8QTSzvsX62gFcVPyzm"), + + // Base addresses + baseBridge: "0xfcde89DFe9276Ec059d68e43759a226f0961426F", + eth: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + erc20: "0x62C1332822983B8412A6Ffda0fd77cd7d5733Ee9", + wSol: "0x4D3210A178De60668986eecfF4eC0B2508eEE1B2", + wSpl: "0xe545c49061424d7F27b642174c95de7c34093b23", + recipient: "0x8c1a617bdb47342f9c17ac8750e0b070c372c721", + counter: "0x5d3eB988Daa06151b68369cf957e917B4371d35d", + }, } as const; diff --git a/solana/scripts/generate-client.ts b/solana/scripts/generate-client.ts new file mode 100644 index 00000000..c9cbc820 --- /dev/null +++ b/solana/scripts/generate-client.ts @@ -0,0 +1,31 @@ +import { $ } from "bun"; +import * as c from "codama"; +import { rootNodeFromAnchor } from "@codama/nodes-from-anchor"; +import { renderVisitor as renderJavaScriptVisitor } from "@codama/renderers-js"; + +async function main() { + const workingDirectory = (await $`pwd`.text()).trim(); + const idlPath = `${workingDirectory}/idl.ts`; + + console.log("=".repeat(40)); + console.log(`Working Directory: ${workingDirectory}`); + console.log(`IDL Path: ${idlPath}`); + console.log("=".repeat(40)); + console.log(""); + + // Instanciate Codama. + const idl = rootNodeFromAnchor(require(idlPath).IDL); + const codama = c.createFromRoot(idl); + + // Render JavaScript. + codama.accept( + renderJavaScriptVisitor(`${workingDirectory}/clients/ts/generated`) + ); + + console.log("✅ Done!"); +} + +await main().catch((error) => { + console.error("❌ Generation failed:", error.message); + process.exit(1); +}); diff --git a/solana/scripts/generate-idl.ts b/solana/scripts/generate-idl.ts new file mode 100644 index 00000000..52cbcf60 --- /dev/null +++ b/solana/scripts/generate-idl.ts @@ -0,0 +1,35 @@ +import { $ } from "bun"; +import { getTarget } from "./utils/argv"; + +async function main() { + const target = getTarget(); + const features = target.split("-").join(","); + + const workingDirectory = (await $`pwd`.text()).trim(); + + console.log("=".repeat(40)); + console.log(`Working Directory: ${workingDirectory}`); + console.log(`Features: ${features}`); + console.log("=".repeat(40)); + console.log(""); + + console.log("📋 Generating IDL..."); + await $`anchor idl build -o ${workingDirectory}/idl.ts -- --features ${features}`; + + console.log("🧹 Removing address key from IDL..."); + const idlFile = Bun.file(`${workingDirectory}/idl.ts`); + const idl = await idlFile.json(); + delete idl.address; + + console.log("⚙️ Converting IDL to typescript..."); + await idlFile.write( + `export const IDL = ${JSON.stringify(idl, null, 2)} as const;` + ); + + console.log("✅ Done!"); +} + +await main().catch((error) => { + console.error("❌ Generation failed:", error.message); + process.exit(1); +}); diff --git a/solana/scripts/initialize.ts b/solana/scripts/initialize.ts deleted file mode 100644 index d8c664b2..00000000 --- a/solana/scripts/initialize.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { PublicKey, SystemProgram } from "@solana/web3.js"; - -import type { Bridge } from "../target/types/bridge"; -import { confirmTransaction } from "./utils/confirm-tx"; -import { getConstantValue } from "./utils/constants"; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - - const tx = await program.methods - .initialize() - .accountsStrict({ - payer: provider.wallet.publicKey, - bridge: bridgePda, - systemProgram: SystemProgram.programId, - }) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/onchain/base-to-solana/prove-message.ts b/solana/scripts/onchain/base-to-solana/prove-message.ts new file mode 100644 index 00000000..f1b1c1a0 --- /dev/null +++ b/solana/scripts/onchain/base-to-solana/prove-message.ts @@ -0,0 +1,179 @@ +import { Endian, getProgramDerivedAddress, getU64Encoder } from "@solana/kit"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { createPublicClient, http, toBytes, type Hash } from "viem"; +import { baseSepolia } from "viem/chains"; +import { decodeEventLog } from "viem/utils"; + +import { + fetchBridge, + getProveMessageInstruction, +} from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { + buildAndSendTransaction, + getPayer, + getRpc, +} from "../utils/transaction"; +import { BRIDGE_ABI } from "../utils/bridge.abi"; + +const TRANSACTION_HASH = + "0x30b961b75231b2711cfd511e9de42aa43096feecd05466356d20bd0e123519f3"; + +async function generateProof( + transactionHash: Hash, + bridgeBaseBlockNumber: bigint, + baseBridgeAddress: string +) { + const publicClient = createPublicClient({ + chain: baseSepolia, + transport: http(), + }); + + const txReceipt = await publicClient.getTransactionReceipt({ + hash: transactionHash, + }); + + // Extract and decode MessageRegistered events + const messageRegisteredEvents = txReceipt.logs + .map((log) => { + try { + const decodedLog = decodeEventLog({ + abi: BRIDGE_ABI, + data: log.data, + topics: log.topics, + }); + + return decodedLog.eventName === "MessageRegistered" + ? { + messageHash: decodedLog.args.messageHash, + mmrRoot: decodedLog.args.mmrRoot, + message: decodedLog.args.message, + } + : null; + } catch (error) { + return null; + } + }) + .filter((event) => event !== null); + + console.log( + `Found ${messageRegisteredEvents.length} MessageRegistered event(s)` + ); + + if (messageRegisteredEvents.length !== 1) { + throw new Error("Unexpected number of MessageRegistered events detected"); + } + + const event = messageRegisteredEvents[0]!; + + console.log("📋 Message Details:"); + console.log(` Hash: ${event.messageHash}`); + console.log(` MMR Root: ${event.mmrRoot}`); + console.log(` Nonce: ${event.message.nonce}`); + console.log(` Sender: ${event.message.sender}`); + console.log(` Data: ${event.message.data}`); + + const [rawProof, totalLeafCount] = await publicClient.readContract({ + address: baseBridgeAddress as `0x${string}`, + abi: BRIDGE_ABI, + functionName: "generateProof", + args: [event.message.nonce], + blockNumber: bridgeBaseBlockNumber, + }); + + console.log(`📊 Proof generated at block ${bridgeBaseBlockNumber}`); + console.log(` Leaf index: ${event.message.nonce}`); + console.log(` Total leaves: ${totalLeafCount}`); + + return { + event, + rawProof, + leafIndex: event.message.nonce, + totalLeafCount, + }; +} + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(); + const rpc = getRpc(target); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const bridge = await fetchBridge(rpc, bridgeAddress); + const baseBlockNumber = bridge.data.baseBlockNumber; + + const [outputRootAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("OUTPUT_ROOT_SEED")), + getU64Encoder({ endian: Endian.Little }).encode(baseBlockNumber), + ], + }); + + const { event, rawProof, leafIndex, totalLeafCount } = await generateProof( + TRANSACTION_HASH, + baseBlockNumber, + constants.baseBridge + ); + + const [messageAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("INCOMING_MESSAGE_SEED")), + toBytes(event.messageHash), + ], + }); + + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`📦 Base Block Number: ${baseBlockNumber}`); + console.log(`🔗 Output Root: ${outputRootAddress}`); + console.log(`🔗 Message: ${messageAddress}`); + console.log(`🔗 Nonce: ${event.message.nonce}`); + console.log(`🔗 Sender: ${event.message.sender}`); + console.log(`🔗 Message Hash: ${event.messageHash}`); + + console.log("🛠️ Building instruction..."); + const ix = getProveMessageInstruction( + { + // Accounts + payer, + outputRoot: outputRootAddress, + message: messageAddress, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + nonce: event.message.nonce, + sender: toBytes(event.message.sender), + data: toBytes(event.message.data), + proof: rawProof.map((e) => toBytes(e)), + leafIndex, + totalLeafCount, + messageHash: toBytes(event.messageHash), + }, + { programAddress: constants.solanaBridge } + ); + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Prove message failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/base-to-solana/relay-message.ts b/solana/scripts/onchain/base-to-solana/relay-message.ts new file mode 100644 index 00000000..63994341 --- /dev/null +++ b/solana/scripts/onchain/base-to-solana/relay-message.ts @@ -0,0 +1,283 @@ +import { + AccountRole, + createKeyPairFromBytes, + createSignerFromKeyPair, + getProgramDerivedAddress, + type IAccountMeta, + type IInstruction, + type TransactionPartialSigner, +} from "@solana/kit"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022"; +import { toBytes, toHex } from "viem"; + +import { + fetchIncomingMessage, + getRelayMessageInstruction, +} from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { + buildAndSendTransaction, + getPayer, + getRpc, +} from "../utils/transaction"; +import { deserializeMessage } from "../utils/deserializer"; + +const MESSAGE_HASH = + "0x5a1e91ae8594a7e58ae2aa213954d7733a5e90b276a37d62800ec00a97e7e66d"; + +const NEW_ACCOUNT_SECRET_KEY = + "0x0cd60f7db0ca726a07da10e35323042a5b05facc00b781e57b06a59eaf2e2197769b26af0c3e3d129796876e465c21b479aae47bba4e9c964bb556d8d7cf93b2"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(); + const rpc = getRpc(target); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const newAccountKeyPair = await createKeyPairFromBytes( + toBytes(NEW_ACCOUNT_SECRET_KEY) + ); + const newAccount = await createSignerFromKeyPair(newAccountKeyPair); + + // Find the message PDA using the message hash (from prove-message) + const [messagePda] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("INCOMING_MESSAGE_SEED")), + toBytes(MESSAGE_HASH), + ], + }); + + // Fetch the message to get the sender for the bridge CPI authority + const message = await fetchIncomingMessage(rpc, messagePda); + + // Find the bridge CPI authority PDA. Not always needed, but simpler to always compute it here. + // It is only really needed if the relayed message needs to CPI into a program that requires + // the bridge CPI authority as a signer. + const [bridgeCpiAuthorityPda] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("BRIDGE_CPI_AUTHORITY_SEED")), + Buffer.from(message.data.sender), + ], + }); + + console.log(`Message PDA: ${messagePda}`); + console.log(`Bridge CPI Authority PDA: ${bridgeCpiAuthorityPda}`); + console.log(`Message executed: ${message.data.executed}`); + console.log(`Message sender: ${toHex(Buffer.from(message.data.sender))}`); + + // if (message.data.executed) { + // console.log("Message has already been executed!"); + // return; + // } + + const messageData = Buffer.from(message.data.data); + const deserializedMessage = await deserializeMessage(messageData); + + let remainingAccounts: Array = []; + const signers: Array = []; + + if (deserializedMessage.type === "Call") { + console.log( + `Call message with ${deserializedMessage.ixs.length} instructions` + ); + + const { ixs } = deserializedMessage; + if (ixs.length === 0) { + throw new Error("Zero instructions in call message"); + } + + // Include both the accounts and program IDs for each instruction + remainingAccounts = [ + ...ixs.flatMap((i) => + i.accounts.map((acc) => ({ + address: acc.address, + role: acc.isWritable + ? acc.isSigner + ? AccountRole.WRITABLE_SIGNER + : AccountRole.WRITABLE + : acc.isSigner + ? AccountRole.READONLY_SIGNER + : AccountRole.READONLY, + })) + ), + ...ixs.map((i) => ({ + address: i.programAddress, + role: AccountRole.READONLY, + })), + ]; + signers.push(newAccount); + } else if (deserializedMessage.type === "Transfer") { + console.log( + `Transfer message with ${deserializedMessage.ixs.length} instructions` + ); + + if (deserializedMessage.transfer.type === "Sol") { + console.log("SOL transfer detected"); + const solTransfer = deserializedMessage.transfer; + + console.log(`SOL transfer:`); + console.log(` Remote token: 0x${toHex(solTransfer.remoteToken)}`); + console.log(` To: ${solTransfer.to}`); + console.log(` Amount: ${solTransfer.amount}`); + + const [solVaultPda] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("SOL_VAULT_SEED")), + Buffer.from(solTransfer.remoteToken), + ], + }); + + remainingAccounts = [ + { + address: solVaultPda, + role: AccountRole.WRITABLE, + }, + { + address: solTransfer.to, + role: AccountRole.WRITABLE, + }, + { + address: SYSTEM_PROGRAM_ADDRESS, + role: AccountRole.READONLY, + }, + ]; + } else if (deserializedMessage.transfer.type === "Spl") { + console.log("SPL transfer detected"); + const splTransfer = deserializedMessage.transfer; + + console.log(`SPL transfer:`); + console.log(` RemoteToken: 0x${toHex(splTransfer.remoteToken)}`); + console.log(` LocalToken: ${splTransfer.localToken}`); + console.log(` To: ${splTransfer.to}`); + console.log(` Amount: ${splTransfer.amount}`); + + const [tokenVaultPda] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("TOKEN_VAULT_SEED")), + splTransfer.localToken, + Buffer.from(splTransfer.remoteToken), + ], + }); + + const mint = await rpc.getAccountInfo(splTransfer.localToken).send(); + if (!mint.value) { + throw new Error("Mint not found"); + } + + remainingAccounts = [ + { + address: splTransfer.localToken, + role: AccountRole.READONLY, + }, + { + address: tokenVaultPda, + role: AccountRole.WRITABLE, + }, + { + address: splTransfer.to, + role: AccountRole.WRITABLE, + }, + { + address: mint.value!.owner, + role: AccountRole.READONLY, + }, + ]; + } else if (deserializedMessage.transfer.type === "WrappedToken") { + const wrappedTransfer = deserializedMessage.transfer; + + console.log(`WrappedToken transfer:`); + console.log(` Local Token: ${wrappedTransfer.localToken}`); + console.log(` To: ${wrappedTransfer.to}`); + console.log(` Amount: ${wrappedTransfer.amount}`); + + remainingAccounts = [ + { + address: wrappedTransfer.localToken, + role: AccountRole.WRITABLE, + }, + { + address: TOKEN_2022_PROGRAM_ADDRESS, + role: AccountRole.READONLY, + }, + ]; + // signers.push(newAccount); + } else { + throw new Error("Unexpected transfer type detected"); + } + + // Process the list of optional instructions + const { ixs } = deserializedMessage; + + // Include both the accounts and program IDs for each instruction + remainingAccounts.push( + ...ixs.flatMap((i) => + i.accounts.map((acc) => ({ + address: acc.address, + role: acc.isWritable + ? acc.isSigner + ? AccountRole.WRITABLE_SIGNER + : AccountRole.WRITABLE + : acc.isSigner + ? AccountRole.READONLY_SIGNER + : AccountRole.READONLY, + })) + ), + ...ixs.map((i) => ({ + address: i.programAddress, + role: AccountRole.READONLY, + })) + ); + } else { + throw new Error("Unexpected message type detected"); + } + + // Set the role to readonly for the bridge CPI authority account (if it exists) + remainingAccounts = remainingAccounts.map((acct) => { + if (acct.address === bridgeCpiAuthorityPda) { + return { + ...acct, + role: AccountRole.READONLY, + }; + } + return acct; + }); + + console.log("🛠️ Building instruction..."); + const ix = getRelayMessageInstruction( + { + payer, + message: messagePda, + }, + { programAddress: constants.solanaBridge } + ); + + const ix_: IInstruction = { + programAddress: ix.programAddress, + accounts: [...ix.accounts, ...remainingAccounts], + data: ix.data, + }; + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix_]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Relay message failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/initialize.ts b/solana/scripts/onchain/initialize.ts new file mode 100644 index 00000000..297dc913 --- /dev/null +++ b/solana/scripts/onchain/initialize.ts @@ -0,0 +1,50 @@ +import { getProgramDerivedAddress } from "@solana/kit"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; + +import { getInitializeInstruction } from "../../clients/ts/generated"; +import { getIdlConstant } from "../utils/idl-constants"; +import { CONSTANTS } from "../constants"; +import { buildAndSendTransaction, getPayer } from "./utils/transaction"; +import { getTarget } from "../utils/argv"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + // Derive the bridge PDA. + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + // Build the instruction. + console.log("🛠️ Building instruction..."); + const ix = getInitializeInstruction( + { + payer: payer, + bridge: bridgeAddress, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + }, + { programAddress: constants.solanaBridge } + ); + + // Send the transaction. + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Initialization failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/solana-to-base/bridge-call-value.ts b/solana/scripts/onchain/solana-to-base/bridge-call-value.ts new file mode 100644 index 00000000..03ac2436 --- /dev/null +++ b/solana/scripts/onchain/solana-to-base/bridge-call-value.ts @@ -0,0 +1,101 @@ +import { + createSignerFromKeyPair, + generateKeyPair, + getBase58Encoder, + getProgramDerivedAddress, +} from "@solana/kit"; +import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { createPublicClient, http, toBytes } from "viem"; +import { baseSepolia } from "viem/chains"; + +import { + CallType, + getBridgeWrappedTokenInstruction, +} from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; +import { BRIDGE_ABI } from "../utils/bridge.abi"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + // Get twin address from Base contract + const publicClient = createPublicClient({ + chain: baseSepolia, + transport: http(), + }); + + const payerBytes = getBase58Encoder().encode(payer.address); + + const twinAddress = await publicClient.readContract({ + address: constants.baseBridge, + abi: BRIDGE_ABI, + functionName: "getPredictedTwinAddress", + args: [`0x${payerBytes.toHex()}`], + }); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const outgoingMessageKeypair = await generateKeyPair(); + const outgoingMessageSigner = await createSignerFromKeyPair( + outgoingMessageKeypair + ); + + console.log(`🔗 Twin: ${twinAddress}`); + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`🔗 From Token Account: ${constants.wEthAta}`); + console.log(`🔗 Outgoing Message: ${outgoingMessageSigner.address}`); + + console.log("🛠️ Building instruction..."); + const ix = getBridgeWrappedTokenInstruction( + { + // Accounts + payer, + from: payer, + gasFeeReceiver: getIdlConstant("GAS_FEE_RECEIVER"), + mint: constants.wEth, + fromTokenAccount: constants.wEthAta, + bridge: bridgeAddress, + outgoingMessage: outgoingMessageSigner, + tokenProgram: TOKEN_2022_PROGRAM_ADDRESS, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + gasLimit: 1_000_000n, + to: toBytes(twinAddress), + amount: 1n, + call: { + ty: CallType.Call, + to: toBytes(constants.counter), + value: 1_000_000_000_000n, // 0.000001 ETH + data: Buffer.from(toBytes("0xd09de08a")), // increment() + }, + }, + { programAddress: constants.solanaBridge } + ); + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Bridge call value failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/solana-to-base/bridge-call.ts b/solana/scripts/onchain/solana-to-base/bridge-call.ts new file mode 100644 index 00000000..17342831 --- /dev/null +++ b/solana/scripts/onchain/solana-to-base/bridge-call.ts @@ -0,0 +1,79 @@ +import { + createSignerFromKeyPair, + generateKeyPair, + getProgramDerivedAddress, +} from "@solana/kit"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { toBytes } from "viem"; + +import { + CallType, + getBridgeCallInstruction, +} from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; + +const COUNTER_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const outgoingMessageKeypair = await generateKeyPair(); + const outgoingMessageSigner = await createSignerFromKeyPair( + outgoingMessageKeypair + ); + + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`🔗 Outgoing Message: ${outgoingMessageSigner.address}`); + + console.log("🛠️ Building instruction..."); + const ix = getBridgeCallInstruction( + { + // Accounts + payer, + from: payer, + gasFeeReceiver: getIdlConstant("GAS_FEE_RECEIVER"), + bridge: bridgeAddress, + outgoingMessage: outgoingMessageSigner, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + gasLimit: 1_000_000n, + call: { + ty: CallType.Call, + to: toBytes(COUNTER_ADDRESS), + value: 0n, + data: Buffer.from("d09de08a", "hex"), + }, + }, + { programAddress: constants.solanaBridge } + ); + + // Send the transaction. + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Bridge call failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/solana-to-base/bridge-sol.ts b/solana/scripts/onchain/solana-to-base/bridge-sol.ts new file mode 100644 index 00000000..99724bae --- /dev/null +++ b/solana/scripts/onchain/solana-to-base/bridge-sol.ts @@ -0,0 +1,82 @@ +import { + createSignerFromKeyPair, + generateKeyPair, + getProgramDerivedAddress, +} from "@solana/kit"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { toBytes } from "viem"; + +import { getBridgeSolInstruction } from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const remoteToken = toBytes(constants.wSol); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const [solVaultAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("SOL_VAULT_SEED")), + Buffer.from(remoteToken), + ], + }); + + const outgoingMessageKeypair = await generateKeyPair(); + const outgoingMessageSigner = await createSignerFromKeyPair( + outgoingMessageKeypair + ); + + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`🔗 Sol Vault: ${solVaultAddress}`); + console.log(`🔗 Outgoing Message: ${outgoingMessageSigner.address}`); + + console.log("🛠️ Building instruction..."); + const ix = getBridgeSolInstruction( + { + // Accounts + payer, + from: payer, + gasFeeReceiver: getIdlConstant("GAS_FEE_RECEIVER"), + solVault: solVaultAddress, + bridge: bridgeAddress, + outgoingMessage: outgoingMessageSigner, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + gasLimit: 1_000_000n, + to: toBytes(constants.recipient), + remoteToken, + amount: BigInt(0.001 * 1e9), + call: null, + }, + { programAddress: constants.solanaBridge } + ); + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Bridge SOL failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/solana-to-base/bridge-spl.ts b/solana/scripts/onchain/solana-to-base/bridge-spl.ts new file mode 100644 index 00000000..ab818618 --- /dev/null +++ b/solana/scripts/onchain/solana-to-base/bridge-spl.ts @@ -0,0 +1,90 @@ +import { + createSignerFromKeyPair, + generateKeyPair, + getBase58Encoder, + getProgramDerivedAddress, +} from "@solana/kit"; +import { TOKEN_PROGRAM_ADDRESS } from "@solana-program/token"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { toBytes } from "viem"; + +import { getBridgeSplInstruction } from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const remoteToken = toBytes(constants.wSpl); + const mintBytes = getBase58Encoder().encode(constants.spl); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const [tokenVaultAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("TOKEN_VAULT_SEED")), + mintBytes, + Buffer.from(remoteToken), + ], + }); + + const outgoingMessageKeypair = await generateKeyPair(); + const outgoingMessageSigner = await createSignerFromKeyPair( + outgoingMessageKeypair + ); + + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`🔗 Token Vault: ${tokenVaultAddress}`); + console.log(`🔗 From Token Account: ${constants.splAta}`); + console.log(`🔗 Outgoing Message: ${outgoingMessageSigner.address}`); + + console.log("🛠️ Building instruction..."); + const ix = getBridgeSplInstruction( + { + // Accounts + payer, + from: payer, + gasFeeReceiver: getIdlConstant("GAS_FEE_RECEIVER"), + mint: constants.spl, + fromTokenAccount: constants.splAta, + tokenVault: tokenVaultAddress, + bridge: bridgeAddress, + outgoingMessage: outgoingMessageSigner, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + gasLimit: 1_000_000n, + to: toBytes(constants.recipient), + remoteToken, + amount: 1n, + call: null, + }, + { programAddress: constants.solanaBridge } + ); + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Bridge SPL failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/solana-to-base/bridge-wrapped-token.ts b/solana/scripts/onchain/solana-to-base/bridge-wrapped-token.ts new file mode 100644 index 00000000..ff94a213 --- /dev/null +++ b/solana/scripts/onchain/solana-to-base/bridge-wrapped-token.ts @@ -0,0 +1,74 @@ +import { + createSignerFromKeyPair, + generateKeyPair, + getProgramDerivedAddress, +} from "@solana/kit"; +import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { toBytes } from "viem"; + +import { getBridgeWrappedTokenInstruction } from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const outgoingMessageKeypair = await generateKeyPair(); + const outgoingMessageSigner = await createSignerFromKeyPair( + outgoingMessageKeypair + ); + + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`🔗 From Token Account: ${constants.wErc20Ata}`); + console.log(`🔗 Outgoing Message: ${outgoingMessageSigner.address}`); + + console.log("🛠️ Building instruction..."); + const ix = getBridgeWrappedTokenInstruction( + { + // Accounts + payer, + from: payer, + gasFeeReceiver: getIdlConstant("GAS_FEE_RECEIVER"), + mint: constants.wErc20, + fromTokenAccount: constants.wErc20Ata, + bridge: bridgeAddress, + outgoingMessage: outgoingMessageSigner, + tokenProgram: TOKEN_2022_PROGRAM_ADDRESS, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + gasLimit: 1_000_000n, + to: toBytes(constants.recipient), + amount: 1n, + call: null, + }, + { programAddress: constants.solanaBridge } + ); + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Bridge wrapped token failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/solana-to-base/wrap-token.ts b/solana/scripts/onchain/solana-to-base/wrap-token.ts new file mode 100644 index 00000000..1917c149 --- /dev/null +++ b/solana/scripts/onchain/solana-to-base/wrap-token.ts @@ -0,0 +1,103 @@ +import { + createSignerFromKeyPair, + generateKeyPair, + getProgramDerivedAddress, + getU8Codec, +} from "@solana/kit"; +import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022"; +import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system"; +import { keccak256, toBytes } from "viem"; + +import { + getWrapTokenInstruction, + type WrapTokenInstructionDataArgs, +} from "../../../clients/ts/generated"; +import { CONSTANTS } from "../../constants"; +import { getTarget } from "../../utils/argv"; +import { getIdlConstant } from "../../utils/idl-constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + const payer = await getPayer(constants.deployerKeyPairFile); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Bridge: ${constants.solanaBridge}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + // Instruction arguments + const args: WrapTokenInstructionDataArgs = { + decimals: 6, + name: "Wrapped ERC20", + symbol: "wERC20", + remoteToken: toBytes(constants.erc20), + scalerExponent: 9, + gasLimit: 1_000_000n, + }; + + // Calculate metadata hash + const metadataHash = keccak256( + Buffer.concat([ + Buffer.from(args.name), + Buffer.from(args.symbol), + Buffer.from(args.remoteToken), + Buffer.from(getU8Codec().encode(args.scalerExponent)), + ]) + ); + + // Derive PDAs + const [mintAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [ + Buffer.from(getIdlConstant("WRAPPED_TOKEN_SEED")), + Buffer.from([args.decimals]), + toBytes(metadataHash), + ], + }); + + const [bridgeAddress] = await getProgramDerivedAddress({ + programAddress: constants.solanaBridge, + seeds: [Buffer.from(getIdlConstant("BRIDGE_SEED"))], + }); + + const outgoingMessageKeypair = await generateKeyPair(); + const outgoingMessageSigner = await createSignerFromKeyPair( + outgoingMessageKeypair + ); + + console.log(`🔗 Bridge: ${bridgeAddress}`); + console.log(`🔗 Mint: ${mintAddress}`); + console.log(`🔗 Outgoing Message: ${outgoingMessageSigner.address}`); + + console.log("🛠️ Building instruction..."); + const ix = getWrapTokenInstruction( + { + // Accounts + payer, + gasFeeReceiver: getIdlConstant("GAS_FEE_RECEIVER"), + mint: mintAddress, + bridge: bridgeAddress, + outgoingMessage: outgoingMessageSigner, + tokenProgram: TOKEN_2022_PROGRAM_ADDRESS, + systemProgram: SYSTEM_PROGRAM_ADDRESS, + + // Arguments + ...args, + }, + { programAddress: constants.solanaBridge } + ); + + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix], payer); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Wrap token failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/spl/create-ata.ts b/solana/scripts/onchain/spl/create-ata.ts new file mode 100644 index 00000000..18d813c8 --- /dev/null +++ b/solana/scripts/onchain/spl/create-ata.ts @@ -0,0 +1,71 @@ +import { + getCreateAssociatedTokenIdempotentInstruction, + findAssociatedTokenPda, + ASSOCIATED_TOKEN_PROGRAM_ADDRESS, +} from "@solana-program/token"; + +import { CONSTANTS } from "../../constants"; +import { + buildAndSendTransaction, + getPayer, + getRpc, +} from "../utils/transaction"; +import { getTarget } from "../../utils/argv"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + + const rpc = getRpc(target); + const payer = await getPayer(constants.deployerKeyPairFile); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const mint = constants.wErc20; + const accountInfo = await rpc + .getAccountInfo(mint, { + encoding: "jsonParsed", + }) + .send(); + if (!accountInfo.value) { + throw new Error("Mint not found"); + } + const tokenProgram = accountInfo.value.owner; + + const [ata] = await findAssociatedTokenPda( + { + owner: payer.address, + tokenProgram, + mint, + }, + { + programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS, + } + ); + + console.log(`🔗 Mint: ${mint}`); + console.log(`🔗 ATA: ${ata}`); + + const ix = getCreateAssociatedTokenIdempotentInstruction({ + payer, + ata, + mint, + owner: payer.address, + tokenProgram, + }); + + // Send the transaction. + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix], payer); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Initialization failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/spl/create-mint.ts b/solana/scripts/onchain/spl/create-mint.ts new file mode 100644 index 00000000..7afe7d5b --- /dev/null +++ b/solana/scripts/onchain/spl/create-mint.ts @@ -0,0 +1,65 @@ +import { createSignerFromKeyPair, generateKeyPair } from "@solana/kit"; +import { getCreateAccountInstruction } from "@solana-program/system"; +import { + getMintSize, + getInitializeMint2Instruction, + TOKEN_PROGRAM_ADDRESS, +} from "@solana-program/token"; + +import { CONSTANTS } from "../../constants"; +import { + buildAndSendTransaction, + getPayer, + getRpc, +} from "../utils/transaction"; +import { getTarget } from "../../utils/argv"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + + const payer = await getPayer(); + const rpc = getRpc(target); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + const mintKeypair = await generateKeyPair(); + const mintSigner = await createSignerFromKeyPair(mintKeypair); + + console.log(`🔗 Mint: ${mintSigner.address}`); + + const space = getMintSize(); + const lamports = await rpc + .getMinimumBalanceForRentExemption(BigInt(space)) + .send(); + + const ixs = [ + getCreateAccountInstruction({ + payer: payer, + newAccount: mintSigner, + lamports, + space, + programAddress: TOKEN_PROGRAM_ADDRESS, + }), + getInitializeMint2Instruction({ + mint: mintSigner.address, + decimals: 10, + mintAuthority: payer.address, + }), + ]; + + // Send the transaction. + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, ixs); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Initialization failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/onchain/spl/mint.ts b/solana/scripts/onchain/spl/mint.ts new file mode 100644 index 00000000..1f6dc9fc --- /dev/null +++ b/solana/scripts/onchain/spl/mint.ts @@ -0,0 +1,40 @@ +import { getMintToInstruction } from "@solana-program/token"; + +import { CONSTANTS } from "../../constants"; +import { buildAndSendTransaction, getPayer } from "../utils/transaction"; +import { getTarget } from "../../utils/argv"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + + const payer = await getPayer(); + + console.log("=".repeat(40)); + console.log(`Target: ${target}`); + console.log(`RPC URL: ${constants.rpcUrl}`); + console.log(`Payer: ${payer.address}`); + console.log("=".repeat(40)); + console.log(""); + + console.log(`🔗 Mint: ${constants.spl}`); + console.log(`🔗 ATA: ${constants.splAta}`); + console.log(`🔗 Mint Authority: ${payer.address}`); + + const ix = getMintToInstruction({ + mint: constants.spl, + token: constants.splAta, + mintAuthority: payer, + amount: 100n, + }); + + // Send the transaction. + console.log("🚀 Sending transaction..."); + await buildAndSendTransaction(target, [ix]); + console.log("✅ Done!"); +} + +main().catch((e) => { + console.error("❌ Initialization failed:", e); + process.exit(1); +}); diff --git a/solana/scripts/utils/bridge.abi.ts b/solana/scripts/onchain/utils/bridge.abi.ts similarity index 100% rename from solana/scripts/utils/bridge.abi.ts rename to solana/scripts/onchain/utils/bridge.abi.ts diff --git a/solana/scripts/utils/buffer-reader.ts b/solana/scripts/onchain/utils/buffer-reader.ts similarity index 84% rename from solana/scripts/utils/buffer-reader.ts rename to solana/scripts/onchain/utils/buffer-reader.ts index 887f6d16..da1edff0 100644 --- a/solana/scripts/utils/buffer-reader.ts +++ b/solana/scripts/onchain/utils/buffer-reader.ts @@ -1,4 +1,4 @@ -import { PublicKey } from "@solana/web3.js"; +import { address, getBase58Codec, type Address } from "@solana/kit"; export class BufferReader { private buffer: Buffer; @@ -32,8 +32,8 @@ export class BufferReader { return value; } - readPublicKey(): PublicKey { - return new PublicKey(this.readBytes(32)); + readAddress(): Address { + return address(getBase58Codec().decode(this.readBytes(32))); } readArray20(): Buffer { diff --git a/solana/scripts/utils/deserializer.ts b/solana/scripts/onchain/utils/deserializer.ts similarity index 68% rename from solana/scripts/utils/deserializer.ts rename to solana/scripts/onchain/utils/deserializer.ts index 01958032..7cc5b6fe 100644 --- a/solana/scripts/utils/deserializer.ts +++ b/solana/scripts/onchain/utils/deserializer.ts @@ -1,11 +1,11 @@ -import { PublicKey } from "@solana/web3.js"; +import { getProgramDerivedAddress, type Address } from "@solana/kit"; import { BufferReader } from "./buffer-reader"; type Message = Call | Transfer; type Call = { type: "Call"; - ixs: ReturnType[]; + ixs: Awaited>[]; }; type Transfer = { @@ -14,27 +14,27 @@ type Transfer = { | { type: "Sol"; remoteToken: Buffer; - to: PublicKey; + to: Address; amount: bigint; } | { type: "Spl"; remoteToken: Buffer; - localToken: PublicKey; - to: PublicKey; + localToken: Address; + to: Address; amount: bigint; } | { type: "WrappedToken"; - localToken: PublicKey; - to: PublicKey; + localToken: Address; + to: Address; amount: bigint; }; - ixs: ReturnType[]; + ixs: Awaited>[]; }; -export function deserializeMessage(buffer: Buffer): Message { +export async function deserializeMessage(buffer: Buffer): Promise { const reader = new BufferReader(buffer); // Read Message enum discriminator (1 byte) @@ -42,7 +42,7 @@ export function deserializeMessage(buffer: Buffer): Message { // Message::Call(Vec) if (messageDiscriminator === 0) { - const ixs = deserializeIxs(reader); + const ixs = await deserializeIxs(reader); return { type: "Call", ixs }; } // Message::Transfer { transfer: Transfer, ixs: Vec } @@ -54,7 +54,7 @@ export function deserializeMessage(buffer: Buffer): Message { // Sol(FinalizeBridgeSol) if (transferDiscriminator === 0) { const remoteToken = reader.readArray20(); // [u8; 20] - const to = reader.readPublicKey(); // Pubkey + const to = reader.readAddress(); // Address const amount = reader.readBigUInt64LE(); // u64 transfer = { type: "Sol", remoteToken, to, amount }; @@ -62,16 +62,16 @@ export function deserializeMessage(buffer: Buffer): Message { // Spl(FinalizeBridgeSpl) else if (transferDiscriminator === 1) { const remoteToken = reader.readArray20(); // [u8; 20] - const localToken = reader.readPublicKey(); // Pubkey (mint) - const to = reader.readPublicKey(); // Pubkey + const localToken = reader.readAddress(); // Address (mint) + const to = reader.readAddress(); // Address const amount = reader.readBigUInt64LE(); // u64 transfer = { type: "Spl", remoteToken, localToken, to, amount }; } // WrappedToken(FinalizeBridgeWrappedToken) else if (transferDiscriminator === 2) { - const localToken = reader.readPublicKey(); // Pubkey (mint) - const to = reader.readPublicKey(); // Pubkey + const localToken = reader.readAddress(); // Address (mint) + const to = reader.readAddress(); // Address const amount = reader.readBigUInt64LE(); // u64 transfer = { type: "WrappedToken", localToken, to, amount }; @@ -84,7 +84,7 @@ export function deserializeMessage(buffer: Buffer): Message { } // Read Vec after the transfer - const ixs = deserializeIxs(reader); + const ixs = await deserializeIxs(reader); return { type: "Transfer", transfer, ixs } as const; } @@ -92,23 +92,23 @@ export function deserializeMessage(buffer: Buffer): Message { throw new Error(`Unknown discriminator: ${messageDiscriminator}`); } -function deserializeIxs(reader: BufferReader) { +async function deserializeIxs(reader: BufferReader) { // Read Vec length (4 bytes) const ixsLength = reader.readUInt32LE(); // Read instructions const ixs = []; for (let i = 0; i < ixsLength; i++) { - const ix = deserializeIx(reader); + const ix = await deserializeIx(reader); ixs.push(ix); } return ixs; } -function deserializeIx(reader: BufferReader) { +async function deserializeIx(reader: BufferReader) { // Read program_id (32 bytes) - const programId = reader.readPublicKey(); + const programAddress = reader.readAddress(); // Read accounts Vec length (4 bytes) const accountsLength = reader.readUInt32LE(); @@ -116,7 +116,7 @@ function deserializeIx(reader: BufferReader) { // Read accounts const accounts = []; for (let i = 0; i < accountsLength; i++) { - const account = deserializeIxAccount(reader); + const account = await deserializeIxAccount(reader); accounts.push(account); } @@ -126,24 +126,24 @@ function deserializeIx(reader: BufferReader) { // Read data const data = reader.readBytes(dataLength); - return { programId, accounts, data }; + return { programAddress, accounts, data }; } -function deserializeIxAccount(reader: BufferReader) { - const pubkey = deserializePubkeyOrPda(reader); +async function deserializeIxAccount(reader: BufferReader) { + const address = await deserializeAddressOrPda(reader); const isWritable = reader.readUInt8() === 1; const isSigner = reader.readUInt8() === 1; - return { pubkey, isWritable, isSigner }; + return { address, isWritable, isSigner }; } -function deserializePubkeyOrPda(reader: BufferReader) { +async function deserializeAddressOrPda(reader: BufferReader) { const discriminator = reader.readUInt8(); // Pubkey variant if (discriminator === 0) { - return reader.readPublicKey(); + return reader.readAddress(); } // PDA variant else if (discriminator === 1) { @@ -156,11 +156,14 @@ function deserializePubkeyOrPda(reader: BufferReader) { seeds.push(seed); } - const programId = reader.readPublicKey(); + const programAddress = reader.readAddress(); // Derive the PDA - const [pubkey] = PublicKey.findProgramAddressSync(seeds, programId); - return pubkey; + const [address] = await getProgramDerivedAddress({ + seeds, + programAddress, + }); + return address; } throw new Error(`Unknown PubkeyOrPda discriminator: ${discriminator}`); diff --git a/solana/scripts/onchain/utils/transaction.ts b/solana/scripts/onchain/utils/transaction.ts new file mode 100644 index 00000000..e6196f01 --- /dev/null +++ b/solana/scripts/onchain/utils/transaction.ts @@ -0,0 +1,78 @@ +import { homedir } from "os"; +import { + appendTransactionMessageInstructions, + createKeyPairFromBytes, + createSignerFromKeyPair, + createSolanaRpc, + createSolanaRpcSubscriptions, + createTransactionMessage, + devnet, + getSignatureFromTransaction, + pipe, + sendAndConfirmTransactionFactory, + setTransactionMessageFeePayer, + setTransactionMessageLifetimeUsingBlockhash, + signTransactionMessageWithSigners, + type IInstruction, + type TransactionSigner, +} from "@solana/kit"; + +import { CONSTANTS } from "../../constants"; +import { fileFromPath } from "../../utils/file"; + +export async function getPayer(keyPairFile?: Bun.BunFile) { + const payerKeyPairFile = keyPairFile + ? keyPairFile + : await fileFromPath(`${homedir()}/.config/solana/id.json`); + + const payerKeyPairBytes = new Uint8Array(await payerKeyPairFile.json()); + const payerKeypair = await createKeyPairFromBytes(payerKeyPairBytes); + return await createSignerFromKeyPair(payerKeypair); +} + +export function getRpc(target: keyof typeof CONSTANTS) { + const constants = CONSTANTS[target]; + return createSolanaRpc(devnet(`https://${constants.rpcUrl}`)); +} + +export async function buildAndSendTransaction( + target: keyof typeof CONSTANTS, + instructions: IInstruction[], + payer?: TransactionSigner +) { + const constants = CONSTANTS[target]; + + const rpc = createSolanaRpc(devnet(`https://${constants.rpcUrl}`)); + const rpcSubscriptions = createSolanaRpcSubscriptions( + devnet(`wss://${constants.rpcUrl}`) + ); + + const sendAndConfirmTx = sendAndConfirmTransactionFactory({ + rpc, + rpcSubscriptions, + }); + + const txPayer = payer ?? (await getPayer()); + const blockhash = await rpc.getLatestBlockhash().send(); + + const transactionMessage = pipe( + createTransactionMessage({ version: 0 }), + (tx) => setTransactionMessageFeePayer(txPayer.address, tx), + (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash.value, tx), + (tx) => appendTransactionMessageInstructions(instructions, tx) + ); + + const signedTransaction = + await signTransactionMessageWithSigners(transactionMessage); + + const signature = getSignatureFromTransaction(signedTransaction); + + await sendAndConfirmTx(signedTransaction, { + commitment: "confirmed", + }); + + console.log( + `✅ Transaction confirmed: https://explorer.solana.com/tx/${signature}?cluster=${constants.cluster}` + ); + return signature; +} diff --git a/solana/scripts/program/build.ts b/solana/scripts/program/build.ts new file mode 100644 index 00000000..511bd2f7 --- /dev/null +++ b/solana/scripts/program/build.ts @@ -0,0 +1,65 @@ +import { $ } from "bun"; +import { type Address } from "@solana/kit"; + +import { fileFromPath } from "../utils/file"; +import { keyPairToAddress } from "../utils/keypair"; +import { CONSTANTS } from "../constants"; +import { getTarget } from "../utils/argv"; + +async function updateLibRs(libRsFile: Bun.BunFile, bridgeAddress: Address) { + const libRs = await libRsFile.text(); + const updatedLibRs = libRs.replace( + /declare_id!\("([^"]+)"\)/, + `declare_id!("${bridgeAddress}")` + ); + + await Bun.write(libRsFile, updatedLibRs); +} + +async function main() { + const target = getTarget(); + const features = target.split("-").join(","); + const constants = CONSTANTS[target]; + + const workingDirectory = (await $`pwd`.text()).trim(); + const libRsFile = await fileFromPath( + `${workingDirectory}/programs/bridge/src/lib.rs` + ); + const libRsBackupFile = await fileFromPath( + `${workingDirectory}/programs/bridge/src/lib.rs.backup`, + false + ); + + const bridgeAddress = await keyPairToAddress(constants.bridgeKeyPairFile); + const deployerAddress = await keyPairToAddress(constants.deployerKeyPairFile); + + console.log("=".repeat(40)); + console.log(`Working Directory: ${workingDirectory}`); + console.log(`Network: ${constants.cluster}`); + console.log(`Environment: ${constants.environment}`); + console.log(`Features: ${features}`); + console.log(`Bridge: ${bridgeAddress}`); + console.log(`Deployer: ${deployerAddress}`); + console.log("=".repeat(40)); + console.log(""); + + console.log("📦 Backing up files..."); + await Bun.write(libRsBackupFile, libRsFile); + + console.log("📝 Updating lib.rs..."); + await updateLibRs(libRsFile, bridgeAddress); + + console.log("🔨 Building program..."); + await $`cargo-build-sbf --features ${features}`; + + console.log("🧹 Restoring lib.rs..."); + await Bun.write(libRsFile, await libRsBackupFile.text()); + await libRsBackupFile.delete(); + + console.log("✅ Done!"); +} + +await main().catch((error) => { + console.error("❌ Build failed:", error.message); + process.exit(1); +}); diff --git a/solana/scripts/program/deploy.ts b/solana/scripts/program/deploy.ts new file mode 100644 index 00000000..6a628225 --- /dev/null +++ b/solana/scripts/program/deploy.ts @@ -0,0 +1,43 @@ +import { $ } from "bun"; +import { CONSTANTS } from "../constants"; + +import { fileFromPath } from "../utils/file"; +import { getTarget } from "../utils/argv"; +import { keyPairToAddress } from "../utils/keypair"; + +async function main() { + const target = getTarget(); + const constants = CONSTANTS[target]; + + const workingDirectory = (await $`pwd`.text()).trim(); + const programFile = await fileFromPath( + `${workingDirectory}/target/deploy/bridge.so` + ); + + const bridgeAddress = await keyPairToAddress(constants.bridgeKeyPairFile); + const deployerAddress = await keyPairToAddress(constants.deployerKeyPairFile); + + console.log("=".repeat(40)); + console.log(`Working Directory: ${workingDirectory}`); + console.log(`Network: ${constants.cluster}`); + console.log(`Environment: ${constants.environment}`); + console.log(`Bridge: ${bridgeAddress}`); + console.log(`Deployer: ${deployerAddress}`); + console.log(`Program Binary: ${programFile.name}`); + console.log("=".repeat(40)); + console.log(""); + + console.log("💰 Checking deployer balance..."); + const balance = + await $`solana balance ${deployerAddress} --url ${constants.cluster}`.text(); + console.log(`Deployer balance: ${balance.trim()}`); + + console.log("🚀 Deploying program..."); + await $`solana program deploy --url ${constants.cluster} --keypair ${constants.deployerKeyPairFile.name} --program-id ${constants.bridgeKeyPairFile.name} ${programFile.name}`; + console.log("✅ Deployment completed successfully!"); +} + +await main().catch((error) => { + console.error("❌ Deployment failed:", error.message); + process.exit(1); +}); diff --git a/solana/scripts/solana-to-base/bridge-call-value.ts b/solana/scripts/solana-to-base/bridge-call-value.ts deleted file mode 100644 index 13ac245f..00000000 --- a/solana/scripts/solana-to-base/bridge-call-value.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { - getAssociatedTokenAddressSync, - TOKEN_2022_PROGRAM_ID, -} from "@solana/spl-token"; -import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; -import { createPublicClient, http, toBytes } from "viem"; -import { baseSepolia } from "viem/chains"; - -import type { Bridge } from "../../target/types/bridge"; -import { BRIDGE_ABI } from "../utils/bridge.abi"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { getConstantValue } from "../utils/constants"; -import { ADDRESSES } from "../addresses"; -import { CONSTANTS } from "../constants"; - -type BridgeWrappedTokenParams = Parameters< - Program["methods"]["bridgeWrappedToken"] ->; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - console.log(`Program ID: ${program.programId.toBase58()}`); - console.log(`Sender: ${provider.wallet.publicKey.toBase58()}`); - - const publicClient = createPublicClient({ - chain: baseSepolia, - transport: http(), - }); - - const twinAddress = await publicClient.readContract({ - address: ADDRESSES.bridge, - abi: BRIDGE_ABI, - functionName: "getPredictedTwinAddress", - args: [`0x${provider.wallet.publicKey.toBuffer().toString("hex")}`], - }); - - console.log(`Twin address: ${twinAddress}`); - - // Ix params - const gasLimit: BridgeWrappedTokenParams[0] = new anchor.BN(1_000_000); - const to: BridgeWrappedTokenParams[1] = toBytes(twinAddress); - const amount: BridgeWrappedTokenParams[2] = new anchor.BN(1); - const call: BridgeWrappedTokenParams[3] = { - ty: { call: {} }, // Call - to: toBytes(CONSTANTS.counterValue), - value: new anchor.BN(1000000000000), - data: Buffer.from(toBytes("0xd09de08a")), // increment() - }; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const bridge = await program.account.bridge.fetch(bridgePda); - - const outgoingMessage = Keypair.generate(); - - // Get user's token account - const mint = new PublicKey(CONSTANTS.wrappedEth); - const fromTokenAccount = getAssociatedTokenAddressSync( - mint, - provider.wallet.publicKey, - false, - TOKEN_2022_PROGRAM_ID - ); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - console.log(`Outgoing message: ${outgoingMessage.publicKey.toBase58()}`); - console.log(`From token account: ${fromTokenAccount.toBase58()}`); - console.log(`Current nonce: ${bridge.nonce.toString()}`); - console.log(`Bridging amount: ${amount.toNumber()}`); - - const tx = await program.methods - .bridgeWrappedToken(gasLimit, to, amount, call) - .accountsStrict({ - payer: provider.wallet.publicKey, - from: provider.wallet.publicKey, - gasFeeReceiver: getConstantValue("gasFeeReceiver"), - mint: mint, - fromTokenAccount: fromTokenAccount, - bridge: bridgePda, - outgoingMessage: outgoingMessage.publicKey, - tokenProgram: TOKEN_2022_PROGRAM_ID, - systemProgram: SystemProgram.programId, - }) - .signers([outgoingMessage]) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/solana-to-base/bridge-call.ts b/solana/scripts/solana-to-base/bridge-call.ts deleted file mode 100644 index f892d3f2..00000000 --- a/solana/scripts/solana-to-base/bridge-call.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; -import { toBytes } from "viem"; - -import type { Bridge } from "../../target/types/bridge"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { getConstantValue } from "../utils/constants"; -import { CONSTANTS } from "../constants"; - -type BridgeCallParams = Parameters["methods"]["bridgeCall"]>; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - // Ix parameters - const gasLimit: BridgeCallParams[0] = new anchor.BN(1000000); - const call: BridgeCallParams[1] = { - ty: { call: {} }, - to: toBytes(CONSTANTS.counterAddress), - value: new anchor.BN(0), - data: Buffer.from("d09de08a", "hex"), // increment() - }; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const bridge = await program.account.bridge.fetch(bridgePda); - - const outgoingMessage = Keypair.generate(); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - console.log(`Outgoing message: ${outgoingMessage.publicKey.toBase58()}`); - console.log(`Current nonce: ${bridge.nonce.toString()}`); - - const tx = await program.methods - .bridgeCall(gasLimit, call) - .accountsStrict({ - payer: provider.wallet.publicKey, - from: provider.wallet.publicKey, - gasFeeReceiver: getConstantValue("gasFeeReceiver"), - bridge: bridgePda, - outgoingMessage: outgoingMessage.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([outgoingMessage]) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/solana-to-base/bridge-sol.ts b/solana/scripts/solana-to-base/bridge-sol.ts deleted file mode 100644 index ea25dcfa..00000000 --- a/solana/scripts/solana-to-base/bridge-sol.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { - Keypair, - PublicKey, - SystemProgram, - LAMPORTS_PER_SOL, -} from "@solana/web3.js"; -import { toBytes } from "viem"; - -import type { Bridge } from "../../target/types/bridge"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { getConstantValue } from "../utils/constants"; -import { ADDRESSES } from "../addresses"; -import { CONSTANTS } from "../constants"; - -type BridgeSolParams = Parameters["methods"]["bridgeSol"]>; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - console.log(`Program ID: ${program.programId.toBase58()}`); - console.log(`Signer: ${provider.wallet.publicKey.toBase58()}`); - - // Ix params - const gasLimit: BridgeSolParams[0] = new anchor.BN(1_000_000); - const to: BridgeSolParams[1] = toBytes(CONSTANTS.recipient); - const remoteToken: BridgeSolParams[2] = toBytes(ADDRESSES.wrappedSOL); - const amount: BridgeSolParams[3] = new anchor.BN( - 0.001 * anchor.web3.LAMPORTS_PER_SOL - ); - const call: BridgeSolParams[4] = null; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const bridge = await program.account.bridge.fetch(bridgePda); - - const [solVaultPda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("solVaultSeed")), Buffer.from(remoteToken)], - program.programId - ); - - const outgoingMessage = Keypair.generate(); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - console.log(`SOL Vault PDA: ${solVaultPda.toBase58()}`); - console.log(`Outgoing message: ${outgoingMessage.publicKey.toBase58()}`); - console.log(`Current nonce: ${bridge.nonce.toString()}`); - console.log(`Bridging ${amount.toNumber() / LAMPORTS_PER_SOL} SOL`); - - const tx = await program.methods - .bridgeSol(gasLimit, to, remoteToken, amount, call) - .accountsStrict({ - payer: provider.wallet.publicKey, - from: provider.wallet.publicKey, - gasFeeReceiver: getConstantValue("gasFeeReceiver"), - solVault: solVaultPda, - bridge: bridgePda, - outgoingMessage: outgoingMessage.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([outgoingMessage]) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - // console.log(e.getLogs()); -}); diff --git a/solana/scripts/solana-to-base/bridge-spl.ts b/solana/scripts/solana-to-base/bridge-spl.ts deleted file mode 100644 index a3d34274..00000000 --- a/solana/scripts/solana-to-base/bridge-spl.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { - getAssociatedTokenAddressSync, - TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; -import { toBytes } from "viem"; - -import type { Bridge } from "../../target/types/bridge"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { getConstantValue } from "../utils/constants"; -import { ADDRESSES } from "../addresses"; -import { CONSTANTS } from "../constants"; - -type BridgeSplParams = Parameters["methods"]["bridgeSpl"]>; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - console.log(`Program ID: ${program.programId.toBase58()}`); - console.log(`Signer: ${provider.wallet.publicKey.toBase58()}`); - - // Ix params - const gasLimit: BridgeSplParams[0] = new anchor.BN(1_000_000); - const to: BridgeSplParams[1] = toBytes(CONSTANTS.recipient); - const remoteToken: BridgeSplParams[2] = toBytes(ADDRESSES.wrappedSPL); - const amount: BridgeSplParams[3] = new anchor.BN(1); - const call: BridgeSplParams[4] = null; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const bridge = await program.account.bridge.fetch(bridgePda); - - const mint = new PublicKey(CONSTANTS.solanaSpl); - const [tokenVaultPda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("tokenVaultSeed")), - mint.toBuffer(), - Buffer.from(remoteToken), - ], - program.programId - ); - - const outgoingMessage = Keypair.generate(); - - const fromTokenAccount = getAssociatedTokenAddressSync( - mint, - provider.wallet.publicKey, - false, - TOKEN_PROGRAM_ID - ); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - console.log(`Token Vault PDA: ${tokenVaultPda.toBase58()}`); - console.log(`Outgoing message: ${outgoingMessage.publicKey.toBase58()}`); - console.log(`From token account: ${fromTokenAccount.toBase58()}`); - console.log(`Current nonce: ${bridge.nonce.toString()}`); - console.log(`Bridging amount: ${amount.toNumber()}`); - - const tx = await program.methods - .bridgeSpl(gasLimit, to, remoteToken, amount, call) - .accountsStrict({ - payer: provider.wallet.publicKey, - from: provider.wallet.publicKey, - gasFeeReceiver: getConstantValue("gasFeeReceiver"), - mint: mint, - fromTokenAccount: fromTokenAccount, - tokenVault: tokenVaultPda, - bridge: bridgePda, - outgoingMessage: outgoingMessage.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - systemProgram: SystemProgram.programId, - }) - .signers([outgoingMessage]) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/solana-to-base/bridge-wrapped-token.ts b/solana/scripts/solana-to-base/bridge-wrapped-token.ts deleted file mode 100644 index f2084c89..00000000 --- a/solana/scripts/solana-to-base/bridge-wrapped-token.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { - getAssociatedTokenAddressSync, - TOKEN_2022_PROGRAM_ID, -} from "@solana/spl-token"; -import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; -import { toBytes } from "viem"; - -import type { Bridge } from "../../target/types/bridge"; -import { CONSTANTS } from "../constants"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { getConstantValue } from "../utils/constants"; - -type BridgeWrappedTokenParams = Parameters< - Program["methods"]["bridgeWrappedToken"] ->; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - console.log(`Program ID: ${program.programId.toBase58()}`); - console.log(`Sender: ${provider.wallet.publicKey.toBase58()}`); - - // Ix params - const gasLimit: BridgeWrappedTokenParams[0] = new anchor.BN(1_000_000); - const to: BridgeWrappedTokenParams[1] = toBytes(CONSTANTS.recipient); - const amount: BridgeWrappedTokenParams[2] = new anchor.BN(1); - const call: BridgeWrappedTokenParams[3] = null; - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const bridge = await program.account.bridge.fetch(bridgePda); - - const outgoingMessage = Keypair.generate(); - - // Get user's token account - const mint = new PublicKey(CONSTANTS.wrappedERC20); - const fromTokenAccount = getAssociatedTokenAddressSync( - mint, - provider.wallet.publicKey, - false, - TOKEN_2022_PROGRAM_ID - ); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - console.log(`Outgoing message: ${outgoingMessage.publicKey.toBase58()}`); - console.log(`From token account: ${fromTokenAccount.toBase58()}`); - console.log(`Current nonce: ${bridge.nonce.toString()}`); - console.log(`Bridging amount: ${amount.toNumber()}`); - - const tx = await program.methods - .bridgeWrappedToken(gasLimit, to, amount, call) - .accountsStrict({ - payer: provider.wallet.publicKey, - from: provider.wallet.publicKey, - gasFeeReceiver: getConstantValue("gasFeeReceiver"), - mint: mint, - fromTokenAccount: fromTokenAccount, - bridge: bridgePda, - outgoingMessage: outgoingMessage.publicKey, - tokenProgram: TOKEN_2022_PROGRAM_ID, - systemProgram: SystemProgram.programId, - }) - .signers([outgoingMessage]) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/solana-to-base/wrap-token.ts b/solana/scripts/solana-to-base/wrap-token.ts deleted file mode 100644 index 65360060..00000000 --- a/solana/scripts/solana-to-base/wrap-token.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; -import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; -import { keccak256, toBytes } from "viem"; - -import type { Bridge } from "../../target/types/bridge"; -import { confirmTransaction } from "../utils/confirm-tx"; -import { getConstantValue } from "../utils/constants"; -import { CONSTANTS } from "../constants"; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Bridge as Program; - - console.log(`Program ID: ${program.programId.toBase58()}`); - console.log(`Signer: ${provider.wallet.publicKey.toBase58()}`); - - // Ix params - const decimals = 6; - const metadata = { - name: "Wrapped ETH", - symbol: "wETH", - remoteToken: toBytes(CONSTANTS.erc20Addr), // Native ETH address on Base - scalerExponent: 12, - }; - const gasLimit = new anchor.BN(1_000_000); - - const metadataHash = keccak256( - Buffer.concat([ - Buffer.from(metadata.name), - Buffer.from(metadata.symbol), - Buffer.from(metadata.remoteToken), - new anchor.BN(metadata.scalerExponent).toBuffer("le", 1), - ]) - ); - - const [mintPda] = PublicKey.findProgramAddressSync( - [ - Buffer.from(getConstantValue("wrappedTokenSeed")), - Buffer.from([decimals]), - toBytes(metadataHash), - ], - program.programId - ); - - const [bridgePda] = PublicKey.findProgramAddressSync( - [Buffer.from(getConstantValue("bridgeSeed"))], - program.programId - ); - - const outgoingMessage = Keypair.generate(); - - console.log(`Bridge PDA: ${bridgePda.toBase58()}`); - console.log(`Mint PDA: ${mintPda.toBase58()}`); - console.log(`Outgoing message: ${outgoingMessage.publicKey.toBase58()}`); - - const tx = await program.methods - .wrapToken(decimals, metadata, gasLimit) - .accountsStrict({ - payer: provider.wallet.publicKey, - gasFeeReceiver: getConstantValue("gasFeeReceiver"), - mint: mintPda, - bridge: bridgePda, - outgoingMessage: outgoingMessage.publicKey, - tokenProgram: TOKEN_2022_PROGRAM_ID, - systemProgram: SystemProgram.programId, - }) - .signers([outgoingMessage]) - .rpc(); - - console.log("Submitted transaction:", tx); - - await confirmTransaction(provider.connection, tx); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/spl/create-mint.ts b/solana/scripts/spl/create-mint.ts deleted file mode 100644 index 22bed735..00000000 --- a/solana/scripts/spl/create-mint.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { createMint, TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { Keypair } from "@solana/web3.js"; - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - - const mint = Keypair.generate(); - console.log(`mint: ${mint.publicKey.toBase58()}`); - - await createMint( - provider.connection, - payer.payer, - payer.publicKey, - payer.publicKey, - 10, - mint, - undefined, - TOKEN_PROGRAM_ID - ); - console.log("Done!"); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/spl/create-user-ata.ts b/solana/scripts/spl/create-user-ata.ts deleted file mode 100644 index 20159436..00000000 --- a/solana/scripts/spl/create-user-ata.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { - getOrCreateAssociatedTokenAccount, - TOKEN_2022_PROGRAM_ID, -} from "@solana/spl-token"; -import { PublicKey } from "@solana/web3.js"; - -import { CONSTANTS } from "../constants"; - -const mint = new PublicKey(CONSTANTS.solanaSpl); - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - - const userATA = await getOrCreateAssociatedTokenAccount( - provider.connection, - payer.payer, - mint, - payer.publicKey, - false, - undefined, - undefined, - TOKEN_2022_PROGRAM_ID - ); - - console.log(`User ATA: ${userATA.address.toBuffer().toString("hex")}`); - console.log("Done!"); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/spl/mint-spl.ts b/solana/scripts/spl/mint-spl.ts deleted file mode 100644 index 9c4029ea..00000000 --- a/solana/scripts/spl/mint-spl.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { - getOrCreateAssociatedTokenAccount, - mintTo, - TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { PublicKey } from "@solana/web3.js"; -import { CONSTANTS } from "../constants"; - -const mint = new PublicKey(CONSTANTS.solanaSpl); - -async function main() { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const amount = 100 * anchor.web3.LAMPORTS_PER_SOL; - const receiver = provider.wallet.publicKey; - - const userATA = await getOrCreateAssociatedTokenAccount( - provider.connection, - payer.payer, - mint, - receiver - ); - - console.log(`User ATA: ${userATA.address.toBuffer().toString("hex")}`); - console.log(`Minting ${amount} tokens to ${payer.publicKey.toBase58()}`); - await mintTo( - provider.connection, - payer.payer, - mint, - userATA.address, - payer.publicKey, - amount, - [], - undefined, - TOKEN_PROGRAM_ID - ); - console.log("Done!"); -} - -main().catch((e) => { - console.error(e); - console.log(e.getLogs()); -}); diff --git a/solana/scripts/utils/argv.ts b/solana/scripts/utils/argv.ts new file mode 100644 index 00000000..a4f06765 --- /dev/null +++ b/solana/scripts/utils/argv.ts @@ -0,0 +1,16 @@ +import { CONSTANTS } from "../constants"; + +export function getTarget() { + const target = process.argv[2] ?? "devnet-alpha"; + console.log(process.argv); + if (!(target in CONSTANTS)) { + console.error( + `Invalid or missing target. Available targets: ${Object.keys( + CONSTANTS + ).join(", ")}` + ); + process.exit(1); + } + + return target as keyof typeof CONSTANTS; +} diff --git a/solana/scripts/utils/confirm-tx.ts b/solana/scripts/utils/confirm-tx.ts deleted file mode 100644 index 4b5c4a4a..00000000 --- a/solana/scripts/utils/confirm-tx.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; - -export async function confirmTransaction( - connection: anchor.web3.Connection, - tx: string -): Promise { - const latestBlockHash = await connection.getLatestBlockhash(); - await connection.confirmTransaction( - { - blockhash: latestBlockHash.blockhash, - lastValidBlockHeight: latestBlockHash.lastValidBlockHeight, - signature: tx, - }, - "confirmed" - ); - - console.log("Confirmed transaction:", tx); -} diff --git a/solana/scripts/utils/env.ts b/solana/scripts/utils/env.ts deleted file mode 100644 index d1a739c7..00000000 --- a/solana/scripts/utils/env.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function env(key: string, allowNullValue?: boolean): string { - const value = process.env[key]; - if (!value && !allowNullValue) { - throw new Error(`${key} not found in env`); - } - return value || ""; -} diff --git a/solana/scripts/utils/file.ts b/solana/scripts/utils/file.ts new file mode 100644 index 00000000..868715b8 --- /dev/null +++ b/solana/scripts/utils/file.ts @@ -0,0 +1,7 @@ +export async function fileFromPath(path: string, mustExist: boolean = true) { + const file = Bun.file(path); + if (mustExist && !(await file.exists())) { + throw new Error(`File not found: ${file.name}`); + } + return file; +} diff --git a/solana/scripts/utils/constants.ts b/solana/scripts/utils/idl-constants.ts similarity index 50% rename from solana/scripts/utils/constants.ts rename to solana/scripts/utils/idl-constants.ts index 395e68fc..d865088a 100644 --- a/solana/scripts/utils/constants.ts +++ b/solana/scripts/utils/idl-constants.ts @@ -1,47 +1,45 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { PublicKey } from "@solana/web3.js"; +import { address, type Address } from "@solana/kit"; +import { IDL } from "../../idl"; -import type { Bridge } from "../../target/types/bridge"; - -type BridgeConstants = Bridge["constants"]; +type BridgeConstants = typeof IDL.constants; type BridgeConstantNames = BridgeConstants[number]["name"]; type BridgeConstant< T extends BridgeConstants, - Name extends BridgeConstantNames + Name extends BridgeConstantNames, > = Extract; type BridgeConstantField< T extends BridgeConstants, Name extends BridgeConstantNames, - Field extends keyof BridgeConstant = "value" + Field extends keyof BridgeConstant = "value", > = BridgeConstant[Field]; type ParsedConstantValue = BridgeConstantField extends "pubkey" - ? PublicKey - : BridgeConstantField extends - | "u64" - | "u16" - | "u8" - ? number - : BridgeConstantField extends "bytes" - ? number[] - : BridgeConstantField extends { - array: any; - } - ? number[] - : BridgeConstantField extends "string" - ? string - : never; + ? Address + : BridgeConstantField extends "u128" | "u64" + ? bigint + : BridgeConstantField extends "u16" | "u8" + ? number + : BridgeConstantField extends "bytes" + ? number[] + : BridgeConstantField extends { + array: any; + } + ? number[] + : BridgeConstantField< + BridgeConstants, + Name, + "type" + > extends "string" + ? string + : never; -export const getConstantValue = ( +export const getIdlConstant = ( name: T ): ParsedConstantValue => { - const program = anchor.workspace.Bridge as Program; - - const constant = program.idl.constants.find((c) => c.name === name); + const constant = IDL.constants.find((c) => c.name === name); if (!constant) { throw new Error(`Constant "${name}" not found`); } @@ -56,13 +54,13 @@ export const getConstantValue = ( // Handle primitive types switch (type) { case "pubkey": - return new PublicKey(value) as unknown as ParsedConstantValue; + return address(value) as unknown as ParsedConstantValue; case "string": - return parseInt(value, 10) as unknown as ParsedConstantValue; + return JSON.parse(value) as unknown as ParsedConstantValue; case "u64": - return parseInt(value, 10) as unknown as ParsedConstantValue; + return BigInt(value) as unknown as ParsedConstantValue; case "bytes": return JSON.parse(value) as unknown as ParsedConstantValue; diff --git a/solana/scripts/utils/keypair.ts b/solana/scripts/utils/keypair.ts new file mode 100644 index 00000000..0ebd320b --- /dev/null +++ b/solana/scripts/utils/keypair.ts @@ -0,0 +1,7 @@ +import { createKeyPairFromBytes, getAddressFromPublicKey } from "@solana/kit"; + +export async function keyPairToAddress(keyPairFile: Bun.BunFile) { + const keyPairBytes = new Uint8Array(await keyPairFile.json()); + const keyPair = await createKeyPairFromBytes(keyPairBytes); + return await getAddressFromPublicKey(keyPair.publicKey); +} diff --git a/solana/scripts/utils/pubkey-to-bytes32.ts b/solana/scripts/utils/pubkey-to-bytes32.ts index 16c1fb63..67be74d7 100644 --- a/solana/scripts/utils/pubkey-to-bytes32.ts +++ b/solana/scripts/utils/pubkey-to-bytes32.ts @@ -1,10 +1,10 @@ -import { PublicKey } from "@solana/web3.js"; +import { getBase58Codec } from "@solana/kit"; +import { CONSTANTS } from "../constants"; -const PUBKEY = "6bmM7CK2yfP4M7KGHmb6Q3b7yCKcdkGQYKszSLwwfpmD"; +const ADDRESS = CONSTANTS["devnet-alpha"].wEthAta; function main() { - const pubKey = new PublicKey(PUBKEY); - const bytes32 = pubKey.toBuffer().toString("hex"); + const bytes32 = getBase58Codec().encode(ADDRESS).toHex(); console.log({ bytes32 }); } From a18d0c935fd75c51fef7eec2dc5f02c3e1e886d6 Mon Sep 17 00:00:00 2001 From: Thanh Trinh Date: Fri, 11 Jul 2025 14:08:43 -0700 Subject: [PATCH 3/3] feat(CHAIN-1645): add Base ISM Verification (#10) * Add ISM Verification + According Unit Test * Adjust unit tests * Make ISM verification upgradable + adjust deploy script * adjust bridge test to relay with correct ismData * Update based on given comments and adjust related file * Update * add a require when removing validator * restructure for ism to be library * remove unnessasry file * adding @notice @ dev for the new library * Update base/src/libraries/ISMVerificationLib.sol Co-authored-by: Jack Chuma * update to seperate initialize from constructor * Update Ownable for Bridge * Update * update description to match * remove submodule * forge fmt * remove unused variable * remove an additional space * fix remaining PRs comment --------- Co-authored-by: Thanh Trinh Co-authored-by: Jack Chuma --- base/script/Deploy.s.sol | 8 +- base/script/HelperConfig.s.sol | 21 +- base/src/Bridge.sol | 200 +++++---- base/src/libraries/ISMVerificationLib.sol | 257 +++++++++++ base/src/libraries/MessageLib.sol | 24 ++ base/test/Bridge.t.sol | 209 +++++---- base/test/ISMVerification.t.sol | 498 ++++++++++++++++++++++ 7 files changed, 1036 insertions(+), 181 deletions(-) create mode 100644 base/src/libraries/ISMVerificationLib.sol create mode 100644 base/src/libraries/MessageLib.sol create mode 100644 base/test/ISMVerification.t.sol diff --git a/base/script/Deploy.s.sol b/base/script/Deploy.s.sol index dfab68a4..ecdd5140 100644 --- a/base/script/Deploy.s.sol +++ b/base/script/Deploy.s.sol @@ -11,6 +11,7 @@ import {Bridge} from "../src/Bridge.sol"; import {CrossChainERC20} from "../src/CrossChainERC20.sol"; import {CrossChainERC20Factory} from "../src/CrossChainERC20Factory.sol"; + import {Twin} from "../src/Twin.sol"; import {HelperConfig} from "./HelperConfig.s.sol"; @@ -65,11 +66,14 @@ contract DeployScript is Script { crossChainErc20Factory: crossChainErc20Factory }); - return ERC1967Factory(cfg.erc1967Factory).deployDeterministic({ + address proxy = ERC1967Factory(cfg.erc1967Factory).deployDeterministicAndCall({ implementation: address(bridgeImpl), admin: cfg.initialOwner, - salt: _salt("bridge15") + salt: _salt("bridge15"), + data: abi.encodeCall(Bridge.initialize, (cfg.initialValidators, cfg.initialThreshold, cfg.initialOwner)) }); + + return proxy; } function _deployFactory(HelperConfig.NetworkConfig memory cfg, address precomputedBridgeAddress) diff --git a/base/script/HelperConfig.s.sol b/base/script/HelperConfig.s.sol index 2cafd451..bc764d18 100644 --- a/base/script/HelperConfig.s.sol +++ b/base/script/HelperConfig.s.sol @@ -14,6 +14,8 @@ contract HelperConfig is Script { Pubkey remoteBridge; address trustedRelayer; address erc1967Factory; + address[] initialValidators; + uint128 initialThreshold; } NetworkConfig private _activeNetworkConfig; @@ -46,11 +48,18 @@ contract HelperConfig is Script { // }); // Public version + address[] memory validators = new address[](3); + validators[0] = 0x20624CA8d0dF80B8bd67C25Bc19A9E10AfB67733; + validators[1] = 0x2880a6DcC8c87dD2874bCBB9ad7E627a407Cf3C2; + validators[2] = 0x0e9a877906EBc3b7098DA2404412BF0Ed1A5EFb4; + return NetworkConfig({ initialOwner: 0x20624CA8d0dF80B8bd67C25Bc19A9E10AfB67733, remoteBridge: Pubkey.wrap(0x9379502b8fd1d9f6feee747094a08cd0f9b79fbbc7e51a36e2da237ee1506460), // AvgDrHpWUeV7fpZYVhDQbWrV2sD7zp9zDB7w97CWknKH trustedRelayer: 0x2880a6DcC8c87dD2874bCBB9ad7E627a407Cf3C2, - erc1967Factory: ERC1967FactoryConstants.ADDRESS + erc1967Factory: ERC1967FactoryConstants.ADDRESS, + initialValidators: validators, + initialThreshold: 2 }); } @@ -61,11 +70,19 @@ contract HelperConfig is Script { ERC1967Factory f = new ERC1967Factory(); + // Use deterministic private keys for validators so tests can sign ISM data + address[] memory validators = new address[](3); + validators[0] = vm.addr(0x1); // VALIDATOR1_KEY + validators[1] = vm.addr(0x2); // VALIDATOR2_KEY + validators[2] = vm.addr(0x3); // VALIDATOR3_KEY + return NetworkConfig({ initialOwner: makeAddr("initialOwner"), remoteBridge: Pubkey.wrap(0xc4c16980efe2a570c1a7599fd2ebb40ca7f85daf897482b9c85d4b8933a61608), trustedRelayer: makeAddr("trustedRelayer"), - erc1967Factory: address(f) + erc1967Factory: address(f), + initialValidators: validators, + initialThreshold: 2 }); } } diff --git a/base/src/Bridge.sol b/base/src/Bridge.sol index 78fad791..032bbb9f 100644 --- a/base/src/Bridge.sol +++ b/base/src/Bridge.sol @@ -1,96 +1,28 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.28; +import {Ownable} from "solady/auth/Ownable.sol"; +import {Initializable} from "solady/utils/Initializable.sol"; import {LibClone} from "solady/utils/LibClone.sol"; import {ReentrancyGuardTransient} from "solady/utils/ReentrancyGuardTransient.sol"; import {UpgradeableBeacon} from "solady/utils/UpgradeableBeacon.sol"; import {Call} from "./libraries/CallLib.sol"; +import {IncomingMessage, MessageType} from "./libraries/MessageLib.sol"; import {MessageStorageLib} from "./libraries/MessageStorageLib.sol"; import {SVMBridgeLib} from "./libraries/SVMBridgeLib.sol"; import {Ix, Pubkey} from "./libraries/SVMLib.sol"; import {SolanaTokenType, TokenLib, Transfer} from "./libraries/TokenLib.sol"; import {Twin} from "./Twin.sol"; +import {ISMVerificationLib} from "./libraries/ISMVerificationLib.sol"; /// @title Bridge /// /// @notice The Bridge enables sending calls from Solana to Base. /// /// @dev Calls sent from Solana to Base are relayed via a Twin contract that is specific per Solana sender pubkey. -contract Bridge is ReentrancyGuardTransient { - ////////////////////////////////////////////////////////////// - /// Events /// - ////////////////////////////////////////////////////////////// - - /// @notice Emitted whenever a message is successfully relayed and executed. - /// - /// @param messageHash Keccak256 hash of the message that was successfully relayed. - event MessageSuccessfullyRelayed(bytes32 indexed messageHash); - - /// @notice Emitted whenever a message fails to be relayed. - /// - /// @param messageHash Keccak256 hash of the message that failed to be relayed. - event FailedToRelayMessage(bytes32 indexed messageHash); - - ////////////////////////////////////////////////////////////// - /// Errors /// - ////////////////////////////////////////////////////////////// - - /// @notice Thrown when the ISM verification fails. - error ISMVerificationFailed(); - - /// @notice Thrown when doing gas estimation and the call's gas left is insufficient to cover the `minGas` plus the - /// `reservedGas`. - error EstimationInsufficientGas(); - - /// @notice Thrown when the call execution fails. - error ExecutionFailed(); - - /// @notice Thrown when the sender is not the entrypoint. - error SenderIsNotEntrypoint(); - - /// @notice Thrown when the nonce is not incremental. - error NonceNotIncremental(); - - /// @notice Thrown when a message has already been successfully relayed. - error MessageAlreadySuccessfullyRelayed(); - - /// @notice Thrown when a message has already failed to relay. - error MessageAlreadyFailedToRelay(); - - /// @notice Thrown when a message has not been marked as failed by the relayer but a user tries to relay it - /// manually. - error MessageNotAlreadyFailedToRelay(); - - /// @notice Thrown when an Anchor instruction is invalid. - error UnsafeIxTarget(); - - ////////////////////////////////////////////////////////////// - /// Structs /// - ////////////////////////////////////////////////////////////// - - /// @notice Enum containing operation types. - enum MessageType { - Call, - Transfer, - TransferAndCall - } - - /// @notice Message sent from Solana to Base. - /// - /// @custom:field nonce Unique nonce for the message. - /// @custom:field sender The Solana sender's pubkey. - /// @custom:field gasLimit The gas limit for the message execution. - /// @custom:field operations The operations to be executed. - struct IncomingMessage { - uint64 nonce; - Pubkey sender; - uint64 gasLimit; - MessageType ty; - bytes data; - } - +contract Bridge is ReentrancyGuardTransient, Initializable, Ownable { ////////////////////////////////////////////////////////////// /// Constants /// ////////////////////////////////////////////////////////////// @@ -166,6 +98,53 @@ contract Bridge is ReentrancyGuardTransient { /// @notice The nonce used for the next incoming message relayed. uint64 public nextIncomingNonce; + ////////////////////////////////////////////////////////////// + /// Events /// + ////////////////////////////////////////////////////////////// + + /// @notice Emitted whenever a message is successfully relayed and executed. + /// + /// @param messageHash Keccak256 hash of the message that was successfully relayed. + event MessageSuccessfullyRelayed(bytes32 indexed messageHash); + + /// @notice Emitted whenever a message fails to be relayed. + /// + /// @param messageHash Keccak256 hash of the message that failed to be relayed. + event FailedToRelayMessage(bytes32 indexed messageHash); + + ////////////////////////////////////////////////////////////// + /// Errors /// + ////////////////////////////////////////////////////////////// + + /// @notice Thrown when the ISM verification fails. + error ISMVerificationFailed(); + + /// @notice Thrown when doing gas estimation and the call's gas left is insufficient to cover the `minGas` plus the + /// `reservedGas`. + error EstimationInsufficientGas(); + + /// @notice Thrown when the call execution fails. + error ExecutionFailed(); + + /// @notice Thrown when the sender is not the entrypoint. + error SenderIsNotEntrypoint(); + + /// @notice Thrown when the nonce is not incremental. + error NonceNotIncremental(); + + /// @notice Thrown when a message has already been successfully relayed. + error MessageAlreadySuccessfullyRelayed(); + + /// @notice Thrown when a message has already failed to relay. + error MessageAlreadyFailedToRelay(); + + /// @notice Thrown when a message has not been marked as failed by the relayer but a user tries to relay it + /// manually. + error MessageNotAlreadyFailedToRelay(); + + /// @notice Thrown when an Anchor instruction is invalid. + error UnsafeIxTarget(); + ////////////////////////////////////////////////////////////// /// Public Functions /// ////////////////////////////////////////////////////////////// @@ -181,6 +160,24 @@ contract Bridge is ReentrancyGuardTransient { TRUSTED_RELAYER = trustedRelayer; TWIN_BEACON = twinBeacon; CROSS_CHAIN_ERC20_FACTORY = crossChainErc20Factory; + + _disableInitializers(); + } + + /// @notice Initializes the Bridge contract with ISM verification parameters. + /// + /// @dev This function should be called immediately after deployment when using with a proxy. + /// Can only be called once due to the initializer modifier. + /// + /// @param validators Array of validator addresses for ISM verification. + /// @param threshold The ISM verification threshold. + /// @param ismOwner The owner of the ISM verification system. + function initialize(address[] calldata validators, uint128 threshold, address ismOwner) external initializer { + // Initialize ownership + _initializeOwner(ismOwner); + + // Initialize ISM verification library + ISMVerificationLib.initialize(validators, threshold); } /// @notice Get the current root of the MMR. @@ -276,7 +273,7 @@ contract Bridge is ReentrancyGuardTransient { function relayMessages(IncomingMessage[] calldata messages, bytes calldata ismData) external nonReentrant { bool isTrustedRelayer = msg.sender == TRUSTED_RELAYER; if (isTrustedRelayer) { - _ismVerify({messages: messages, ismData: ismData}); + require(ISMVerificationLib.isApproved(messages, ismData), ISMVerificationFailed()); } for (uint256 i; i < messages.length; i++) { @@ -381,6 +378,49 @@ contract Bridge is ReentrancyGuardTransient { } } + /// @notice Sets the ISM verification threshold. + /// + /// @param newThreshold The new ISM verification threshold. + function setISMThreshold(uint128 newThreshold) external onlyOwner { + ISMVerificationLib.setThreshold(newThreshold); + } + + /// @notice Add a validator to the ISM verification set. + /// + /// @param validator Address to add as validator. + function addISMValidator(address validator) external onlyOwner { + ISMVerificationLib.addValidator(validator); + } + + /// @notice Remove a validator from the ISM verification set. + /// + /// @param validator Address to remove. + function removeISMValidator(address validator) external onlyOwner { + ISMVerificationLib.removeValidator(validator); + } + + /// @notice Gets the current ISM verification threshold. + /// + /// @return The current threshold. + function getISMThreshold() external view returns (uint128) { + return ISMVerificationLib.getThreshold(); + } + + /// @notice Gets the current ISM validator count. + /// + /// @return The current validator count. + function getISMValidatorCount() external view returns (uint128) { + return ISMVerificationLib.getValidatorCount(); + } + + /// @notice Checks if an address is an ISM validator. + /// + /// @param validator The address to check. + /// @return True if the address is a validator, false otherwise. + function isISMValidator(address validator) external view returns (bool) { + return ISMVerificationLib.isValidator(validator); + } + ////////////////////////////////////////////////////////////// /// Internal Functions /// ////////////////////////////////////////////////////////////// @@ -400,16 +440,4 @@ contract Bridge is ReentrancyGuardTransient { function _assertSenderIsEntrypoint() private view { require(msg.sender == address(this), SenderIsNotEntrypoint()); } - - /// @notice Checks whether the ISM verification is successful. - /// - /// @param messages The messages to verify. - /// @param ismData Encoded ISM data used to verify the call. - function _ismVerify(IncomingMessage[] calldata messages, bytes calldata ismData) private pure { - messages; // Silence unused variable warning. - ismData; // Silence unused variable warning. - - // TODO: Plug some ISM verification here. - require(true, ISMVerificationFailed()); - } } diff --git a/base/src/libraries/ISMVerificationLib.sol b/base/src/libraries/ISMVerificationLib.sol new file mode 100644 index 00000000..558e1afd --- /dev/null +++ b/base/src/libraries/ISMVerificationLib.sol @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import {IncomingMessage} from "./MessageLib.sol"; + +/// @notice Storage layout used by this library. +/// +/// @custom:storage-location erc7201:coinbase.storage.ISMVerificationLib +/// +/// @custom:field validators Mapping of validator addresses to their status. +/// @custom:field threshold ISM verification threshold. +/// @custom:field validatorCount Count of validators. +struct ISMVerificationLibStorage { + mapping(address => bool) validators; + uint128 threshold; + uint128 validatorCount; +} + +/// @title ISMVerificationLib +/// +/// @notice A verification contract for ISM Messages being broadcasted from Solana to Base by requiring +/// a specific minimum amount of validators to sign the message. +/// +/// @dev This contract is only relevant for Stage 0 of the bridge where offchain oracle handles the relaying +/// of messages. This contract should be irrelevant for Stage 1, where messages will automatically be +/// included by the Base sequencer. +library ISMVerificationLib { + ////////////////////////////////////////////////////////////// + /// Constants /// + ////////////////////////////////////////////////////////////// + + /// @notice The length of a signature in bytes. + uint256 public constant SIGNATURE_LENGTH_THRESHOLD = 65; + + /// @dev Slot for the `ISMVerificationLibStorage` struct in storage. + /// Computed from: + /// keccak256(abi.encode(uint256(keccak256("coinbase.storage.ISMVerificationLib")) - 1)) & + /// ~bytes32(uint256(0xff)) + /// + /// Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201). + bytes32 private constant _ISM_VERIFICATION_LIB_STORAGE_LOCATION = + 0x5c4e6f8b5a3b8e9a2c1f4d7e8a9b6c3d2e1f0a8b7c4d5e6f9a2b3c4d5e6f7a8b; + + ////////////////////////////////////////////////////////////// + /// Events /// + ////////////////////////////////////////////////////////////// + + /// @notice Emitted whenever the threshold is updated. + event ThresholdUpdated(uint256 newThreshold); + + /// @notice Emitted whenever a validator is added. + event ValidatorAdded(address validator); + + /// @notice Emitted whenever a validator is removed. + event ValidatorRemoved(address validator); + + ////////////////////////////////////////////////////////////// + /// Errors /// + ////////////////////////////////////////////////////////////// + + /// @notice Thrown when threshold is 0. + error InvalidThreshold(); + + /// @notice Thrown when the signature length is invalid. + error InvalidSignatureLength(); + + /// @notice Thrown when a validator address is 0. + error InvalidValidatorAddress(); + + /// @notice Thrown when a validator is already added. + error ValidatorAlreadyAdded(); + + /// @notice Thrown when a validator is not a validator. + error ValidatorNotExisted(); + + /// @notice Thrown when signatures are not in ascending order. + error InvalidSignatureOrder(); + + /// @notice Thrown when ISM data is empty. + error EmptyISMData(); + + /// @notice Thrown when validator count is less than threshold. + error ValidatorCountLessThanThreshold(); + + ////////////////////////////////////////////////////////////// + /// Internal Functions /// + ////////////////////////////////////////////////////////////// + + /// @notice Helper function to get a storage reference to the `ISMVerificationLibStorage` struct. + /// + /// @return $ A storage reference to the `ISMVerificationLibStorage` struct. + function getISMVerificationLibStorage() internal pure returns (ISMVerificationLibStorage storage $) { + assembly ("memory-safe") { + $.slot := _ISM_VERIFICATION_LIB_STORAGE_LOCATION + } + } + + /// @notice Initializes the ISM verification library. + /// + /// @param validators Array of validator addresses. + /// @param threshold The ISM verification threshold. + function initialize(address[] calldata validators, uint128 threshold) internal { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + + require(threshold > 0 && threshold <= validators.length, InvalidThreshold()); + + for (uint128 i = 0; i < validators.length; i++) { + require(validators[i] != address(0), InvalidValidatorAddress()); + require(!$.validators[validators[i]], ValidatorAlreadyAdded()); + $.validators[validators[i]] = true; + } + $.validatorCount = uint128(validators.length); + $.threshold = threshold; + } + + /// @notice Sets the ISM verification threshold. + /// + /// @param newThreshold The new ISM verification threshold. + function setThreshold(uint128 newThreshold) internal { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + require(newThreshold > 0 && newThreshold <= $.validatorCount, InvalidThreshold()); + + $.threshold = newThreshold; + + emit ThresholdUpdated(newThreshold); + } + + /// @notice Add a validator to the set + /// + /// @param validator Address to add as validator + function addValidator(address validator) internal { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + require(validator != address(0), InvalidValidatorAddress()); + require(!$.validators[validator], ValidatorAlreadyAdded()); + + $.validators[validator] = true; + + unchecked { + $.validatorCount++; + } + + emit ValidatorAdded(validator); + } + + /// @notice Remove a validator from the set + /// + /// @param validator Address to remove + function removeValidator(address validator) internal { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + require($.validators[validator], ValidatorNotExisted()); + require($.validatorCount - 1 >= $.threshold, ValidatorCountLessThanThreshold()); + + $.validators[validator] = false; + + unchecked { + $.validatorCount--; + } + + emit ValidatorRemoved(validator); + } + + /// @notice Verifies the ISM by checking M-of-N validator signatures. + /// + /// @param messages The messages to be verified. + /// @param ismData The ISM data containing concatenated signatures. + /// + /// @return True if the ISM is verified, false otherwise. + function isApproved(IncomingMessage[] calldata messages, bytes calldata ismData) internal view returns (bool) { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + + // Check that the provided signature data is not too short + require(ismData.length >= $.threshold * SIGNATURE_LENGTH_THRESHOLD, InvalidSignatureLength()); + + uint256 offset; + assembly { + offset := ismData.offset + } + + // Compute hash of the messages being verified + bytes32 messageHash = keccak256(abi.encode(messages)); + // There cannot be a validator with address 0 + address lastValidator = address(0); + + // Verify M-of-N signatures + for (uint256 i = 0; i < $.threshold; i++) { + (uint8 v, bytes32 r, bytes32 s) = signatureSplit(offset, i); + + // Standard ECDSA signature recovery + address currentValidator = ecrecover(messageHash, v, r, s); + + // Check for duplicate signers + if (currentValidator == lastValidator) { + return false; + } + + // Ensure ascending order + if (currentValidator < lastValidator) { + return false; + } + + // Verify signer is a registered validator + if (!$.validators[currentValidator]) { + return false; + } + + lastValidator = currentValidator; + } + + return true; + } + + /// @notice Gets the current threshold. + /// + /// @return The current threshold. + function getThreshold() internal view returns (uint128) { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + return $.threshold; + } + + /// @notice Gets the current validator count. + /// + /// @return The current validator count. + function getValidatorCount() internal view returns (uint128) { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + return $.validatorCount; + } + + /// @notice Checks if an address is a validator. + /// + /// @param validator The address to check. + /// @return True if the address is a validator, false otherwise. + function isValidator(address validator) internal view returns (bool) { + ISMVerificationLibStorage storage $ = getISMVerificationLibStorage(); + return $.validators[validator]; + } + + /// @notice Splits signature bytes into v, r, s components + /// + /// @param signaturesCalldataOffset Calldata offset where signatures bytes starts + /// @param pos Position of signature to split (0-indexed) + /// + /// @return v The recovery id + /// @return r The r component of the signature + /// @return s The s component of the signature + function signatureSplit(uint256 signaturesCalldataOffset, uint256 pos) + internal + pure + returns (uint8 v, bytes32 r, bytes32 s) + { + assembly { + let signaturePos := mul(0x41, pos) // 65 bytes per signature + r := calldataload(add(signaturesCalldataOffset, signaturePos)) // r at offset 0 + s := calldataload(add(signaturesCalldataOffset, add(signaturePos, 0x20))) // s at offset 32 + v := and(calldataload(add(signaturesCalldataOffset, add(signaturePos, 0x21))), 0xff) // v at offset 64 + } + } +} diff --git a/base/src/libraries/MessageLib.sol b/base/src/libraries/MessageLib.sol new file mode 100644 index 00000000..1c3d819a --- /dev/null +++ b/base/src/libraries/MessageLib.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.8.28; + +import {Pubkey} from "./SVMLib.sol"; + +/// @notice Enum containing operation types. +enum MessageType { + Call, + Transfer, + TransferAndCall +} + +/// @notice Message sent from Solana to Base. +/// +/// @custom:field nonce Unique nonce for the message. +/// @custom:field sender The Solana sender's pubkey. +/// @custom:field gasLimit The gas limit for the message execution. +/// @custom:field operations The operations to be executed. +struct IncomingMessage { + uint64 nonce; + Pubkey sender; + uint64 gasLimit; + MessageType ty; + bytes data; +} diff --git a/base/test/Bridge.t.sol b/base/test/Bridge.t.sol index 40a5b388..22b108b0 100644 --- a/base/test/Bridge.t.sol +++ b/base/test/Bridge.t.sol @@ -9,8 +9,10 @@ import {HelperConfig} from "../script/HelperConfig.s.sol"; import {Bridge} from "../src/Bridge.sol"; import {CrossChainERC20} from "../src/CrossChainERC20.sol"; import {CrossChainERC20Factory} from "../src/CrossChainERC20Factory.sol"; + import {Twin} from "../src/Twin.sol"; import {Call, CallType} from "../src/libraries/CallLib.sol"; +import {IncomingMessage, MessageType} from "../src/libraries/MessageLib.sol"; import {MessageStorageLib} from "../src/libraries/MessageStorageLib.sol"; import {SVMBridgeLib} from "../src/libraries/SVMBridgeLib.sol"; import {Ix, Pubkey} from "../src/libraries/SVMLib.sol"; @@ -41,7 +43,13 @@ contract BridgeTest is Test { event MessageSuccessfullyRelayed(bytes32 indexed messageHash); event FailedToRelayMessage(bytes32 indexed messageHash); + // Test validator keys for ISM verification (same as in HelperConfig) + uint256 constant VALIDATOR1_KEY = 0x1; + uint256 constant VALIDATOR2_KEY = 0x2; + uint256 constant VALIDATOR3_KEY = 0x3; + function setUp() public { + // Use the DeployScript normally - now it uses deterministic validator keys DeployScript deployer = new DeployScript(); (twinBeacon, bridge, factory, helperConfig) = deployer.run(); @@ -67,24 +75,6 @@ contract BridgeTest is Test { mockToken.mint(address(bridge), 1000e18); } - ////////////////////////////////////////////////////////////// - /// Constructor Tests /// - ////////////////////////////////////////////////////////////// - - function test_constructor_setsCorrectValues() public { - Bridge testBridge = new Bridge({ - remoteBridge: TEST_SENDER, - trustedRelayer: trustedRelayer, - twinBeacon: address(twinBeacon), - crossChainErc20Factory: address(factory) - }); - - assertEq(Pubkey.unwrap(testBridge.REMOTE_BRIDGE()), Pubkey.unwrap(TEST_SENDER)); - assertEq(testBridge.TRUSTED_RELAYER(), trustedRelayer); - assertEq(testBridge.TWIN_BEACON(), address(twinBeacon)); - assertEq(testBridge.nextIncomingNonce(), 0); - } - ////////////////////////////////////////////////////////////// /// Bridge Call Tests /// ////////////////////////////////////////////////////////////// @@ -211,12 +201,12 @@ contract BridgeTest is Test { ////////////////////////////////////////////////////////////// function test_relayMessages_withTrustedRelayer() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -227,7 +217,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -238,12 +228,12 @@ contract BridgeTest is Test { function test_relayMessages_withNonTrustedRelayer() public { // First, make a message fail from trusted relayer - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 100000, // Insufficient gas to force failure - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -254,7 +244,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -263,18 +253,18 @@ contract BridgeTest is Test { messages[0].gasLimit = 1000000; vm.prank(unauthorizedUser); - bridge.relayMessages(messages, ismData); + bridge.relayMessages(messages, hex""); // Non-trusted relayer doesn't need valid ISM data assertEq(mockTarget.value(), 42); } function test_relayMessages_revertsOnIncrementalNonce() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 1, // Should be 0 sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -285,7 +275,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.expectRevert(Bridge.NonceNotIncremental.selector); vm.prank(trustedRelayer); @@ -294,12 +284,12 @@ contract BridgeTest is Test { function test_relayMessages_revertsOnAlreadySuccessfulMessage() public { // First, create a message that will succeed with trusted relayer - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -310,7 +300,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); // First attempt by trusted relayer should succeed vm.prank(trustedRelayer); @@ -320,16 +310,16 @@ contract BridgeTest is Test { // MessageAlreadySuccessfullyRelayed vm.expectRevert(Bridge.MessageAlreadySuccessfullyRelayed.selector); vm.prank(unauthorizedUser); - bridge.relayMessages(messages, ismData); + bridge.relayMessages(messages, hex""); // Non-trusted relayer doesn't need valid ISM data } function test_relayMessages_emitsSuccessEvent() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -343,7 +333,7 @@ contract BridgeTest is Test { bytes32 expectedHash = keccak256(abi.encode(messages[0].nonce, messages[0].sender, messages[0].ty, messages[0].data)); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.expectEmit(true, false, false, false); emit MessageSuccessfullyRelayed(expectedHash); @@ -353,12 +343,12 @@ contract BridgeTest is Test { } function test_relayMessages_emitsFailureEvent() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -372,7 +362,7 @@ contract BridgeTest is Test { bytes32 expectedHash = keccak256(abi.encode(messages[0].nonce, messages[0].sender, messages[0].ty, messages[0].data)); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.expectEmit(true, false, false, false); emit FailedToRelayMessage(expectedHash); @@ -386,12 +376,12 @@ contract BridgeTest is Test { ////////////////////////////////////////////////////////////// function test_relayMessage_callType() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -402,7 +392,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -423,16 +413,16 @@ contract BridgeTest is Test { remoteAmount: 100e6 }); - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Transfer, + ty: MessageType.Transfer, data: abi.encode(transfer) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -456,16 +446,16 @@ contract BridgeTest is Test { data: abi.encodeWithSelector(MockTarget.setValue.selector, 456) }); - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.TransferAndCall, + ty: MessageType.TransferAndCall, data: abi.encode(transfer, call) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -475,16 +465,16 @@ contract BridgeTest is Test { } function test_relayMessage_remoteBridgeSpecialCase() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: remoteBridge, // Special case gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode(address(mockToken), TEST_REMOTE_TOKEN, uint8(12)) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -498,11 +488,11 @@ contract BridgeTest is Test { ////////////////////////////////////////////////////////////// function test_validateAndRelay_revertsOnDirectCall() public { - Bridge.IncomingMessage memory message = Bridge.IncomingMessage({ + IncomingMessage memory message = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -519,11 +509,11 @@ contract BridgeTest is Test { } function test_relayMessage_revertsOnDirectCall() public { - Bridge.IncomingMessage memory message = Bridge.IncomingMessage({ + IncomingMessage memory message = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -545,12 +535,12 @@ contract BridgeTest is Test { function test_gasEstimation_revertsOnFailure() public { // First make this message fail by trusted relayer so it can be retried - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 100000, // Low gas to cause failure - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -561,7 +551,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer, bridge.ESTIMATION_ADDRESS()); vm.expectRevert(Bridge.ExecutionFailed.selector); @@ -788,8 +778,8 @@ contract BridgeTest is Test { ////////////////////////////////////////////////////////////// function test_relayMessages_withEmptyArray() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](0); - bytes memory ismData = hex""; + IncomingMessage[] memory messages = new IncomingMessage[](0); + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -799,13 +789,13 @@ contract BridgeTest is Test { } function test_relayMessages_withMultipleMessages() public { - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](3); + IncomingMessage[] memory messages = new IncomingMessage[](3); for (uint256 i = 0; i < 3; i++) { - messages[i] = Bridge.IncomingMessage({ + messages[i] = IncomingMessage({ nonce: uint64(i), sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -817,7 +807,7 @@ contract BridgeTest is Test { }); } - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -828,12 +818,12 @@ contract BridgeTest is Test { function test_twinReuse() public { // First message creates Twin - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: 0, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -844,7 +834,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -862,8 +852,11 @@ contract BridgeTest is Test { }) ); + // Regenerate ISM data for the modified message + bytes memory ismData2 = _generateValidISMData(messages); + vm.prank(trustedRelayer); - bridge.relayMessages(messages, ismData); + bridge.relayMessages(messages, ismData2); address secondTwin = bridge.twins(TEST_SENDER); assertEq(firstTwin, secondTwin); @@ -893,12 +886,12 @@ contract BridgeTest is Test { // Increment the nonce naturally by sending messages for (uint64 i = 0; i < nonce; i++) { - Bridge.IncomingMessage[] memory tempMessages = new Bridge.IncomingMessage[](1); - tempMessages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory tempMessages = new IncomingMessage[](1); + tempMessages[0] = IncomingMessage({ nonce: i, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -909,18 +902,18 @@ contract BridgeTest is Test { ) }); - bytes memory tempIsmData = hex""; + bytes memory tempIsmData = _generateValidISMData(tempMessages); vm.prank(trustedRelayer); bridge.relayMessages(tempMessages, tempIsmData); } // Now send the actual test message - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: nonce, sender: TEST_SENDER, gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: abi.encode( Call({ ty: CallType.Call, @@ -931,7 +924,7 @@ contract BridgeTest is Test { ) }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -955,16 +948,16 @@ contract BridgeTest is Test { }); bytes memory data = abi.encode(call); - Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage[](1); - messages[0] = Bridge.IncomingMessage({ + IncomingMessage[] memory messages = new IncomingMessage[](1); + messages[0] = IncomingMessage({ nonce: bridge.nextIncomingNonce(), sender: remoteBridge, // Only remote bridge can register tokens gasLimit: 1000000, - ty: Bridge.MessageType.Call, + ty: MessageType.Call, data: data }); - bytes memory ismData = hex""; + bytes memory ismData = _generateValidISMData(messages); vm.prank(trustedRelayer); bridge.relayMessages(messages, ismData); @@ -1162,6 +1155,40 @@ contract BridgeTest is Test { console2.log("Leaf1 should have leaf2 as sibling:", vm.toString(expectedLeaf2)); console2.log("Leaf2 should have leaf1 as sibling:", vm.toString(expectedLeaf1)); } + + ////////////////////////////////////////////////////////////// + /// Internal Functions /// + ////////////////////////////////////////////////////////////// + + /// @notice Generates valid ISM data for a given set of messages. + /// + /// @param messages The messages to be included in the ISM. + /// + /// @return The ISM data containing the signatures of the validators. + function _generateValidISMData(IncomingMessage[] memory messages) internal pure returns (bytes memory) { + bytes32 messageHash = keccak256(abi.encode(messages)); + + // Create signatures from validators (threshold = 2, so we need 2 signatures) + bytes memory signatures = new bytes(0); + + // Sort validators by address to ensure ascending order + uint256[] memory sortedKeys = new uint256[](2); + sortedKeys[0] = VALIDATOR1_KEY; + sortedKeys[1] = VALIDATOR2_KEY; + + if (vm.addr(VALIDATOR1_KEY) > vm.addr(VALIDATOR2_KEY)) { + sortedKeys[0] = VALIDATOR2_KEY; + sortedKeys[1] = VALIDATOR1_KEY; + } + + // Create signatures in ascending order + for (uint256 i = 0; i < 2; i++) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(sortedKeys[i], messageHash); + signatures = abi.encodePacked(signatures, r, s, v); + } + + return signatures; + } } ////////////////////////////////////////////////////////////// diff --git a/base/test/ISMVerification.t.sol b/base/test/ISMVerification.t.sol new file mode 100644 index 00000000..4976b25d --- /dev/null +++ b/base/test/ISMVerification.t.sol @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +import {HelperConfig} from "../script/HelperConfig.s.sol"; +import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; +import {ERC1967Factory} from "solady/utils/ERC1967Factory.sol"; +import {UpgradeableBeacon} from "solady/utils/UpgradeableBeacon.sol"; + +import {Bridge} from "../src/Bridge.sol"; + +import {CrossChainERC20} from "../src/CrossChainERC20.sol"; +import {CrossChainERC20Factory} from "../src/CrossChainERC20Factory.sol"; +import {Twin} from "../src/Twin.sol"; +import {IncomingMessage, MessageType} from "../src/libraries/MessageLib.sol"; +import {Pubkey} from "../src/libraries/SVMLib.sol"; + +contract ISMVerificationTest is Test { + Bridge public bridge; + + // Test accounts + address public owner; + address public validator1; + address public validator2; + address public validator3; + address public validator4; + address public nonValidator; + address public trustedRelayer; + + // Test private keys for signing + uint256 public constant VALIDATOR1_KEY = 0x1; + uint256 public constant VALIDATOR2_KEY = 0x2; + uint256 public constant VALIDATOR3_KEY = 0x3; + uint256 public constant VALIDATOR4_KEY = 0x4; + + // Test messages + IncomingMessage[] internal testMessages; + + // Events to test + event ISMVerified(); + + function setUp() public { + HelperConfig helperConfig = new HelperConfig(); + HelperConfig.NetworkConfig memory cfg = helperConfig.getConfig(); + + owner = makeAddr("owner"); + validator1 = vm.addr(VALIDATOR1_KEY); + validator2 = vm.addr(VALIDATOR2_KEY); + validator3 = vm.addr(VALIDATOR3_KEY); + validator4 = vm.addr(VALIDATOR4_KEY); + nonValidator = makeAddr("nonValidator"); + + // Deploy Bridge with ISM validators and threshold + address[] memory validators = new address[](4); + validators[0] = validator1; + validators[1] = validator2; + validators[2] = validator3; + validators[3] = validator4; + + // Deploy supporting contracts first + Pubkey remoteBridge = Pubkey.wrap(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef); + trustedRelayer = makeAddr("trustedRelayer"); + + // Create Twin beacon + address twinImpl = address(new Twin(address(0))); // Placeholder, will be updated + address twinBeacon = address(new UpgradeableBeacon(owner, twinImpl)); + + // Create CrossChainERC20Factory + address erc20Impl = address(new CrossChainERC20(address(0))); // Placeholder + address erc20Beacon = address(new UpgradeableBeacon(owner, erc20Impl)); + CrossChainERC20Factory factory = new CrossChainERC20Factory(erc20Beacon); + + // Deploy Bridge + vm.prank(owner); + Bridge bridgeImpl = new Bridge({ + remoteBridge: remoteBridge, + trustedRelayer: trustedRelayer, + twinBeacon: twinBeacon, + crossChainErc20Factory: address(factory) + }); + + vm.prank(owner); + address bridgeAddr = ERC1967Factory(cfg.erc1967Factory).deployDeterministicAndCall({ + implementation: address(bridgeImpl), + admin: owner, + salt: _salt(bytes12("bridge")), + data: abi.encodeCall(Bridge.initialize, (validators, 2, owner)) + }); + + bridge = Bridge(bridgeAddr); + + // Create test messages + testMessages.push( + IncomingMessage({ + nonce: 0, + sender: Pubkey.wrap(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef), + gasLimit: 1000000, + ty: MessageType.Call, + data: hex"deadbeef" + }) + ); + + testMessages.push( + IncomingMessage({ + nonce: 1, + sender: Pubkey.wrap(0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890), + gasLimit: 500000, + ty: MessageType.Transfer, + data: hex"cafebabe" + }) + ); + } + + ////////////////////////////////////////////////////////////// + /// Constructor Tests /// + ////////////////////////////////////////////////////////////// + + function test_constructor_setsCorrectThreshold() public view { + assertEq(bridge.getISMThreshold(), 2); + } + + function test_constructor_setsOwner() public view { + assertEq(bridge.owner(), owner); + } + + function test_constructor_setsValidators() public view { + // Check that all validators are correctly set + assertTrue(bridge.isISMValidator(validator1)); + assertTrue(bridge.isISMValidator(validator2)); + assertTrue(bridge.isISMValidator(validator3)); + assertTrue(bridge.isISMValidator(validator4)); + assertFalse(bridge.isISMValidator(nonValidator)); // Should not be a validator + } + + function test_constructor_setsValidatorCount() public view { + assertEq(bridge.getISMValidatorCount(), 4); + } + + function test_constructor_revertsWithInvalidThreshold() public { + address[] memory validators = new address[](2); + validators[0] = validator1; + validators[1] = validator2; + + // Deploy supporting contracts first + Pubkey remoteBridge = Pubkey.wrap(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef); + address twinImpl = address(new Twin(address(0))); + address twinBeacon = address(new UpgradeableBeacon(owner, twinImpl)); + address erc20Impl = address(new CrossChainERC20(address(0))); + address erc20Beacon = address(new UpgradeableBeacon(owner, erc20Impl)); + CrossChainERC20Factory factory = new CrossChainERC20Factory(erc20Beacon); + + // Test threshold = 0 + Bridge testBridge1 = new Bridge({ + remoteBridge: remoteBridge, + trustedRelayer: trustedRelayer, + twinBeacon: twinBeacon, + crossChainErc20Factory: address(factory) + }); + + vm.expectRevert(); // Library will revert with InvalidThreshold + testBridge1.initialize({validators: validators, threshold: 0, ismOwner: owner}); + + // Test threshold > validator count + Bridge testBridge2 = new Bridge({ + remoteBridge: remoteBridge, + trustedRelayer: trustedRelayer, + twinBeacon: twinBeacon, + crossChainErc20Factory: address(factory) + }); + + vm.expectRevert(); // Library will revert with InvalidThreshold + testBridge2.initialize({validators: validators, threshold: 3, ismOwner: owner}); + } + + function test_constructor_revertsWithEmptyValidatorsAndNonZeroThreshold() public { + address[] memory validators = new address[](0); + + // Deploy supporting contracts first + Pubkey remoteBridge = Pubkey.wrap(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef); + address twinImpl = address(new Twin(address(0))); + address twinBeacon = address(new UpgradeableBeacon(owner, twinImpl)); + address erc20Impl = address(new CrossChainERC20(address(0))); + address erc20Beacon = address(new UpgradeableBeacon(owner, erc20Impl)); + CrossChainERC20Factory factory = new CrossChainERC20Factory(erc20Beacon); + + Bridge testBridge3 = new Bridge({ + remoteBridge: remoteBridge, + trustedRelayer: trustedRelayer, + twinBeacon: twinBeacon, + crossChainErc20Factory: address(factory) + }); + + vm.expectRevert(); // Library will revert with InvalidThreshold + testBridge3.initialize({validators: validators, threshold: 1, ismOwner: owner}); + } + + function test_constructor_allowsEmptyValidatorsWithZeroThreshold() public { + address[] memory validators = new address[](0); + + // Deploy supporting contracts first + Pubkey remoteBridge = Pubkey.wrap(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef); + address twinImpl = address(new Twin(address(0))); + address twinBeacon = address(new UpgradeableBeacon(owner, twinImpl)); + address erc20Impl = address(new CrossChainERC20(address(0))); + address erc20Beacon = address(new UpgradeableBeacon(owner, erc20Impl)); + CrossChainERC20Factory factory = new CrossChainERC20Factory(erc20Beacon); + + // This should actually fail based on the current validation logic + Bridge testBridge4 = new Bridge({ + remoteBridge: remoteBridge, + trustedRelayer: trustedRelayer, + twinBeacon: twinBeacon, + crossChainErc20Factory: address(factory) + }); + + vm.expectRevert(); // Library will revert with InvalidThreshold + testBridge4.initialize({validators: validators, threshold: 0, ismOwner: owner}); + } + + ////////////////////////////////////////////////////////////// + /// Validator Management Tests /// + ////////////////////////////////////////////////////////////// + + function test_addValidator_addsValidatorCorrectly() public { + address newValidator = makeAddr("newValidator"); + + vm.prank(owner); + bridge.addISMValidator(newValidator); + + assertTrue(bridge.isISMValidator(newValidator)); + assertEq(bridge.getISMValidatorCount(), 5); // 4 + 1 + } + + function test_addValidator_revertsIfAlreadyValidator() public { + vm.prank(owner); + vm.expectRevert(); // Library will revert with ValidatorAlreadyAdded + bridge.addISMValidator(validator1); + } + + function test_removeValidator_removesValidatorCorrectly() public { + vm.prank(owner); + bridge.removeISMValidator(validator1); + + assertFalse(bridge.isISMValidator(validator1)); + assertEq(bridge.getISMValidatorCount(), 3); // 4 - 1 + } + + function test_removeValidator_revertsIfNotValidator() public { + vm.prank(owner); + vm.expectRevert(); // Library will revert with ValidatorNotExisted + bridge.removeISMValidator(nonValidator); + } + + ////////////////////////////////////////////////////////////// + /// Threshold Management Tests /// + ////////////////////////////////////////////////////////////// + + function test_setThreshold_setsCorrectThreshold() public { + vm.prank(owner); + bridge.setISMThreshold(3); + + assertEq(bridge.getISMThreshold(), 3); + } + + function test_setThreshold_revertsIfZero() public { + vm.prank(owner); + vm.expectRevert(); // Library will revert with InvalidThreshold + bridge.setISMThreshold(0); + } + + function test_setThreshold_revertsIfGreaterThanValidatorCount() public { + vm.prank(owner); + vm.expectRevert(); // Library will revert with InvalidThreshold + bridge.setISMThreshold(5); // Greater than 4 validators + } + + function test_setThreshold_revertsIfNotOwner() public { + vm.prank(nonValidator); + vm.expectRevert(); + bridge.setISMThreshold(3); + } + + ////////////////////////////////////////////////////////////// + /// ISM Verification Tests /// + ////////////////////////////////////////////////////////////// + + function test_verifyISM_withValidSignatures() public { + // Create message hash + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Create signatures (threshold = 2, so we need 2 signatures) + bytes memory signatures = _createValidSignatures(messageHash, 2); + + // Verify ISM through relayMessages - should succeed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_withThresholdSignatures() public { + // Set threshold to 3 + vm.prank(owner); + bridge.setISMThreshold(3); + + bytes32 messageHash = keccak256(abi.encode(testMessages)); + bytes memory signatures = _createValidSignatures(messageHash, 3); + + // Verify ISM through relayMessages - should succeed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithInsufficientSignatures() public { + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Only provide 1 signature when threshold is 2 + bytes memory signatures = _createValidSignatures(messageHash, 1); + + vm.expectRevert(); // Library will revert with InvalidSignatureLength + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithInvalidSignature() public { + // Create malformed signature (wrong length) + bytes memory signatures = new bytes(64); // Should be 65 bytes per signature + + vm.expectRevert(); // Library will revert with InvalidSignatureLength + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithNonValidatorSigner() public { + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Create signature from non-validator + uint256 nonValidatorKey = 0x999; + bytes memory signatures = _createSignature(messageHash, nonValidatorKey); + + // Add a valid validator signature to meet length requirement + bytes memory validSig = _createSignature(messageHash, VALIDATOR1_KEY); + signatures = abi.encodePacked(signatures, validSig); + + vm.expectRevert(); // Library will revert with ISMVerificationFailed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithDuplicateSigners() public { + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Create duplicate signatures from same validator + bytes memory sig1 = _createSignature(messageHash, VALIDATOR1_KEY); + bytes memory sig2 = _createSignature(messageHash, VALIDATOR1_KEY); + bytes memory signatures = abi.encodePacked(sig1, sig2); + + vm.expectRevert(); // Library will revert with ISMVerificationFailed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithWrongMessageHash() public { + // Create signatures for different messages + IncomingMessage[] memory differentMessages = new IncomingMessage[](1); + differentMessages[0] = IncomingMessage({ + nonce: 0, + sender: Pubkey.wrap(0x9999999999999999999999999999999999999999999999999999999999999999), + gasLimit: 999999, + ty: MessageType.Call, + data: hex"99999999" + }); + + bytes32 differentMessageHash = keccak256(abi.encode(differentMessages)); + bytes memory signatures = _createValidSignatures(differentMessageHash, 2); + + // Try to verify with original messages (different hash) + vm.expectRevert(); // Library will revert with ISMVerificationFailed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_withAscendingOrderSignatures() public { + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Ensure signatures are in ascending order of addresses + address[] memory sortedValidators = new address[](2); + uint256[] memory sortedKeys = new uint256[](2); + + if (validator1 < validator2) { + sortedValidators[0] = validator1; + sortedValidators[1] = validator2; + sortedKeys[0] = VALIDATOR1_KEY; + sortedKeys[1] = VALIDATOR2_KEY; + } else { + sortedValidators[0] = validator2; + sortedValidators[1] = validator1; + sortedKeys[0] = VALIDATOR2_KEY; + sortedKeys[1] = VALIDATOR1_KEY; + } + + bytes memory signatures = + abi.encodePacked(_createSignature(messageHash, sortedKeys[0]), _createSignature(messageHash, sortedKeys[1])); + + // Verify ISM through relayMessages - should succeed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithDescendingOrderSignatures() public { + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Ensure signatures are in descending order (should fail) + address[] memory sortedValidators = new address[](2); + uint256[] memory sortedKeys = new uint256[](2); + + if (validator1 > validator2) { + sortedValidators[0] = validator1; + sortedValidators[1] = validator2; + sortedKeys[0] = VALIDATOR1_KEY; + sortedKeys[1] = VALIDATOR2_KEY; + } else { + sortedValidators[0] = validator2; + sortedValidators[1] = validator1; + sortedKeys[0] = VALIDATOR2_KEY; + sortedKeys[1] = VALIDATOR1_KEY; + } + + bytes memory signatures = + abi.encodePacked(_createSignature(messageHash, sortedKeys[0]), _createSignature(messageHash, sortedKeys[1])); + + vm.expectRevert(); // Library will revert with ISMVerificationFailed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + function test_verifyISM_revertsWithInvalidSignatureOrder() public { + bytes32 messageHash = keccak256(abi.encode(testMessages)); + + // Create signatures in wrong order by deliberately choosing validators with descending addresses + uint256 higherKey = validator1 > validator2 ? VALIDATOR1_KEY : VALIDATOR2_KEY; + uint256 lowerKey = validator1 > validator2 ? VALIDATOR2_KEY : VALIDATOR1_KEY; + + // Create signatures with higher address first (descending order) + bytes memory signatures = + abi.encodePacked(_createSignature(messageHash, higherKey), _createSignature(messageHash, lowerKey)); + + vm.expectRevert(); // Library will revert with ISMVerificationFailed + vm.prank(trustedRelayer); + bridge.relayMessages(testMessages, signatures); + } + + ////////////////////////////////////////////////////////////// + /// Helper Functions /// + ////////////////////////////////////////////////////////////// + + function _createValidSignatures(bytes32 messageHash, uint256 numSignatures) internal pure returns (bytes memory) { + require(numSignatures <= 4, "Too many signatures requested"); + + uint256[] memory keys = new uint256[](4); + keys[0] = VALIDATOR1_KEY; + keys[1] = VALIDATOR2_KEY; + keys[2] = VALIDATOR3_KEY; + keys[3] = VALIDATOR4_KEY; + + // Sort keys by their corresponding addresses to ensure ascending order + for (uint256 i = 0; i < keys.length - 1; i++) { + for (uint256 j = i + 1; j < keys.length; j++) { + if (vm.addr(keys[i]) > vm.addr(keys[j])) { + uint256 temp = keys[i]; + keys[i] = keys[j]; + keys[j] = temp; + } + } + } + + bytes memory signatures = new bytes(0); + for (uint256 i = 0; i < numSignatures; i++) { + signatures = abi.encodePacked(signatures, _createSignature(messageHash, keys[i])); + } + + return signatures; + } + + function _createSignature(bytes32 messageHash, uint256 privateKey) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, messageHash); + return abi.encodePacked(r, s, v); + } + + function _createInvalidSignature() internal pure returns (bytes memory) { + // Create a signature with invalid length + return new bytes(64); // Should be 65 bytes + } + + function _salt(bytes12 salt) private view returns (bytes32) { + // Concat the owner (who will be the caller via vm.prank) and the salt + bytes memory packed = abi.encodePacked(owner, salt); + return bytes32(packed); + } +}