From 59e0e586ef215349a0e0a1a5ebdaabb836b34d4d Mon Sep 17 00:00:00 2001 From: Jordan Sexton Date: Mon, 6 Sep 2021 19:50:38 -0500 Subject: [PATCH] Allow Sollet asset migration Change-Id: Id3fde0ef232fa2e8ed1435be34fe2b885ecd2ef7 --- solana/modules/token_bridge/Cargo.lock | 51 +++++++ .../modules/token_bridge/program/Cargo.toml | 3 + .../program/src/api/complete_transfer.rs | 28 +++- .../program/src/api/create_wrapped.rs | 124 +++++++++++++++--- .../token_bridge/program/src/api/tokens.json | 112 ++++++++++++++++ .../token_bridge/program/src/api/tokens.txt | 44 +++++++ .../token_bridge/program/src/api/transfer.rs | 25 +++- .../token_bridge/program/src/instructions.rs | 27 +--- .../modules/token_bridge/program/src/types.rs | 1 + .../token_bridge/program/tests/integration.rs | 83 ++++++++++++ 10 files changed, 453 insertions(+), 45 deletions(-) create mode 100644 solana/modules/token_bridge/program/src/api/tokens.json create mode 100644 solana/modules/token_bridge/program/src/api/tokens.txt diff --git a/solana/modules/token_bridge/Cargo.lock b/solana/modules/token_bridge/Cargo.lock index e7b73e3af..2f011c4ae 100644 --- a/solana/modules/token_bridge/Cargo.lock +++ b/solana/modules/token_bridge/Cargo.lock @@ -1942,6 +1942,50 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "phf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +dependencies = [ + "phf_shared", + "rand 0.8.4", +] + +[[package]] +name = "phf_macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2 1.0.28", + "quote 1.0.9", + "syn 1.0.75", +] + +[[package]] +name = "phf_shared" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -2556,6 +2600,12 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335" +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + [[package]] name = "slab" version = "0.4.4" @@ -3390,6 +3440,7 @@ dependencies = [ "hex", "hex-literal", "libsecp256k1", + "phf", "primitive-types", "rand 0.7.3", "rocksalt", diff --git a/solana/modules/token_bridge/program/Cargo.toml b/solana/modules/token_bridge/program/Cargo.toml index 4ffeeeb1d..db17bb342 100644 --- a/solana/modules/token_bridge/program/Cargo.toml +++ b/solana/modules/token_bridge/program/Cargo.toml @@ -14,6 +14,7 @@ trace = ["solitaire/trace"] wasm = ["no-entrypoint"] client = ["solitaire-client", "solitaire/client", "no-entrypoint"] cpi = ["no-entrypoint"] +test = [] default = [] [dependencies] @@ -31,6 +32,8 @@ spl-token-metadata = { path = "../token-metadata" } wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } serde = { version = "1.0", features = ["derive"] } rand = { version = "0.7.3", optional = true } +hex = "0.4.3" +phf = { version = "0.9", default-features = false, features=["macros"] } [dev-dependencies] hex = "*" diff --git a/solana/modules/token_bridge/program/src/api/complete_transfer.rs b/solana/modules/token_bridge/program/src/api/complete_transfer.rs index 202196697..3f2e1f28c 100644 --- a/solana/modules/token_bridge/program/src/api/complete_transfer.rs +++ b/solana/modules/token_bridge/program/src/api/complete_transfer.rs @@ -12,6 +12,7 @@ use crate::{ WrappedMint, WrappedTokenMeta, }, + api::derive_mint_for_token, messages::PayloadTransfer, types::*, TokenBridgeError::*, @@ -241,6 +242,27 @@ pub fn complete_wrapped( accs.vaa.verify(ctx.program_id)?; accs.vaa.claim(ctx, accs.payer.key)?; + let (_, is_sollet) = + derive_mint_for_token(ctx.program_id, accs.vaa.token_address, accs.vaa.token_chain); + let (amount, fee) = if is_sollet && accs.wrapped_meta.original_decimals > 6 { + // Sollet assets are truncated to 6 decimals, however Wormhole uses 8 and assumes + // wire-truncation to 8 decimals. + ( + accs.vaa + .amount + .as_u64() + .checked_div(10u64.pow(2.min(accs.wrapped_meta.original_decimals as u32 - 6))) + .unwrap(), + accs.vaa + .fee + .as_u64() + .checked_div(10u64.pow(2.min(accs.wrapped_meta.original_decimals as u32 - 6))) + .unwrap(), + ) + } else { + (accs.vaa.amount.as_u64(), accs.vaa.fee.as_u64()) + }; + // Mint tokens let mint_ix = spl_token::instruction::mint_to( &spl_token::id(), @@ -248,11 +270,7 @@ pub fn complete_wrapped( accs.to.info().key, accs.mint_authority.key, &[], - accs.vaa - .amount - .as_u64() - .checked_sub(accs.vaa.fee.as_u64()) - .unwrap(), + amount.checked_sub(fee).unwrap(), )?; invoke_seeded(&mint_ix, ctx, &accs.mint_authority, None)?; diff --git a/solana/modules/token_bridge/program/src/api/create_wrapped.rs b/solana/modules/token_bridge/program/src/api/create_wrapped.rs index f202dc6a4..3b6444630 100644 --- a/solana/modules/token_bridge/program/src/api/create_wrapped.rs +++ b/solana/modules/token_bridge/program/src/api/create_wrapped.rs @@ -13,8 +13,13 @@ use crate::{ }, messages::PayloadAssetMeta, types::*, + TokenBridgeError::InvalidMint, }; -use bridge::vaa::ClaimableVAA; +use bridge::{ + api::ForeignAddress, + vaa::ClaimableVAA, +}; +use phf::phf_map; use solana_program::{ account_info::AccountInfo, program::invoke_signed, @@ -38,10 +43,12 @@ use spl_token::{ }; use std::{ cmp::min, + convert::TryInto, ops::{ Deref, DerefMut, }, + str::FromStr, }; #[derive(FromAccounts)] @@ -53,7 +60,7 @@ pub struct CreateWrapped<'b> { pub vaa: ClaimableVAA<'b, PayloadAssetMeta>, // New Wrapped - pub mint: Mut>, + pub mint: Mut>, pub meta: Mut>, /// SPL Metadata for the associated Mint @@ -94,14 +101,94 @@ impl<'b> InstructionContext<'b> for CreateWrapped<'b> { #[derive(BorshDeserialize, BorshSerialize, Default)] pub struct CreateWrappedData {} +#[cfg(not(feature = "test"))] +pub static SOLLET_MINTS: phf::Map<&str, (u16, &str)> = phf_map! { + // "WETH", + "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" => (2, "2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk"), + // "YFI", + "0000000000000000000000000bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e" => (2, "3JSf5tPeuscJGtaCp5giEiDhv51gQ4v3zWg8DGgyLfAB"), + // "LINK", + "000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca" => (2, "CWE8jPTUYhdCTZYWPTe1o5DFqfdjzWKc9WKz6rSjQUdG"), + // "SUSHI", + "0000000000000000000000006b3595068778dd592e39a122f4f5a5cf09c90fe2" => (2, "AR1Mtgh7zAtxuxGd2XPovXPVjcSdY3i4rQYisNadjfKy"), + // "ALEPH", + "00000000000000000000000027702a26126e0B3702af63Ee09aC4d1A084EF628" => (2, "CsZ5LZkDS7h9TDKjrbL7VAwQZ9nsRu8vJLhRYfmGaN8K"), + // "SXP", + "0000000000000000000000008ce9137d39326ad0cd6491fb5cc0cba0e089b6a9" => (2, "SF3oTvfWzEP3DTwGSvUXRrGTvr75pdZNnBLAH9bzMuX"), + // "CREAM", + "0000000000000000000000002ba592F78dB6436527729929AAf6c908497cB200" => (2, "5Fu5UUgbjpUvdBveb3a1JTNirL8rXtiYeSMWvKjtUNQv"), + // "FRONT", + "000000000000000000000000f8C3527CC04340b208C854E985240c02F7B7793f" => (2, "9S4t2NEAiJVMvPdRYKVrfJpBafPBLtvbvyS3DecojQHw"), + // "AKRO", + "0000000000000000000000008ab7404063ec4dbcfd4598215992dc3f8ec853d7" => (2, "6WNVCuxCGJzNjmMZoKyhZJwvJ5tYpsLyAtagzYASqBoF"), + // "HXRO", + "0000000000000000000000004bd70556ae3f8a6ec6c4080a0c327b24325438f3" => (2, "DJafV9qemGp7mLMEn5wrfqaFwxsbLgUsGVS16zKRk9kc"), + // "UNI", + "0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984" => (2, "DEhAasscXF4kEGxFgJ3bq4PpVGp5wyUxMRvn6TzGVHaw"), + // "FTT", + "00000000000000000000000050d1c9771902476076ecfc8b2a83ad6b9355a4c9" => (2, "AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3"), + // "LUA", + "000000000000000000000000b1f66997a5760428d3a87d68b90bfe0ae64121cc" => (2, "EqWCKXfs3x47uVosDpTRgFniThL9Y8iCztJaapxbEaVX"), + // "MATH", + "00000000000000000000000008d967bb0134f2d07f7cfb6e246680c53927dd30" => (2, "GeDS162t9yGJuLEHPWXXGrb1zwkzinCgRwnT8vHYjKza"), + // "KEEP", + "00000000000000000000000085eee30c52b0b379b046fb0f85f4f3dc3009afec" => (2, "GUohe4DJUA5FKPWo3joiPgsB7yzer7LpDmt1Vhzy3Zht"), + // "SWAG", + "00000000000000000000000087eDfFDe3E14c7a66c9b9724747a1C5696b742e6" => (2, "9F9fNTT6qwjsu4X4yWYKZpsbw5qT7o6yR2i57JF2jagy"), + // "CEL", + "000000000000000000000000aaaebe6fe48e54f431b0c390cfaf0b017d09d42d" => (2, "DgHK9mfhMtUwwv54GChRrU54T2Em5cuszq2uMuen1ZVE"), + // "RSR", + "0000000000000000000000008762db106b2c2a0bccb3a80d1ed41273552616e8" => (2, "7ncCLJpP3MNww17LW8bRvx8odQQnubNtfNZBL5BgAEHW"), + // "1INCH", + "000000000000000000000000111111111117dc0aa78b770fa6a738034120c302" => (2, "5wihEYGca7X4gSe97C5mVcqNsfxBzhdTwpv72HKs25US"), + // "GRT", + "000000000000000000000000c944e90c64b2c07662a292be6244bdf05cda44a7" => (2, "38i2NQxjp5rt5B3KogqrxmBxgrAwaB3W1f1GmiKqh9MS"), + // "COMP", + "000000000000000000000000c00e94cb662c3520282e6f5717214004a7f26888" => (2, "Avz2fmevhhu87WYtWQCFj9UjKRjF9Z9QWwN2ih9yF95G"), + // "PAXG", + "00000000000000000000000045804880De22913dAFE09f4980848ECE6EcbAf78" => (2, "9wRD14AhdZ3qV8et3eBQVsrb3UoBZDUbJGyFckpTg8sj"), +}; + +#[cfg(feature = "test")] +pub static SOLLET_MINTS: phf::Map<&str, (u16, &str)> = phf_map! { + // "TEST", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => (2, "FDhdMYh3KsF64Jxzh8tnx9rJXQTcN461rguUK9z9zm64"), +}; + +pub fn derive_mint_for_token( + program_id: &Pubkey, + token_address: Address, + token_chain: ChainID, +) -> (Pubkey, bool) { + let mut sollet_mint = SOLLET_MINTS.get(hex::encode(token_address).as_str()); + if let Some(mint) = sollet_mint { + if mint.0 == token_chain { + return (Pubkey::from_str(mint.1).unwrap(), true); + } + } + + ( + WrappedMint::<'_, { AccountState::Uninitialized }>::key( + &WrappedDerivationData { + token_chain, + token_address, + }, + &program_id, + ), + false, + ) +} + pub fn create_wrapped( ctx: &ExecutionContext, accs: &mut CreateWrapped, data: CreateWrappedData, ) -> Result<()> { - let derivation_data: WrappedDerivationData = (&*accs).into(); - accs.mint - .verify_derivation(ctx.program_id, &derivation_data)?; + let (mint, is_sollet) = + derive_mint_for_token(ctx.program_id, accs.vaa.token_address, accs.vaa.token_chain); + if *accs.mint.info().key != mint { + return Err(InvalidMint.into()); + } let meta_derivation_data: WrappedMetaDerivationData = (&*accs).into(); accs.meta @@ -114,19 +201,21 @@ pub fn create_wrapped( accs.vaa.verify(ctx.program_id)?; accs.vaa.claim(ctx, accs.payer.key)?; - // Create mint account - accs.mint - .create(&((&*accs).into()), ctx, accs.payer.key, Exempt)?; + if !is_sollet { + // Create mint account + accs.mint + .create(&((&*accs).into()), ctx, accs.payer.key, Exempt)?; - // Initialize mint - let init_ix = spl_token::instruction::initialize_mint( - &spl_token::id(), - accs.mint.info().key, - accs.mint_authority.key, - None, - min(8, accs.vaa.decimals), // Limit to 8 decimals, truncation is handled on the other side - )?; - invoke_signed(&init_ix, ctx.accounts, &[])?; + // Initialize mint + let init_ix = spl_token::instruction::initialize_mint( + &spl_token::id(), + accs.mint.info().key, + accs.mint_authority.key, + None, + min(8, accs.vaa.decimals), // Limit to 8 decimals, truncation is handled on the other side + )?; + invoke_signed(&init_ix, ctx.accounts, &[])?; + } // Create meta account accs.meta @@ -166,6 +255,7 @@ pub fn create_wrapped( // Populate meta account accs.meta.chain = accs.vaa.token_chain; accs.meta.token_address = accs.vaa.token_address; + accs.meta.original_decimals = accs.vaa.decimals; Ok(()) } diff --git a/solana/modules/token_bridge/program/src/api/tokens.json b/solana/modules/token_bridge/program/src/api/tokens.json new file mode 100644 index 000000000..5d9207656 --- /dev/null +++ b/solana/modules/token_bridge/program/src/api/tokens.json @@ -0,0 +1,112 @@ +[ + { + "address": "2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk", + "symbol": "ETH", + "ethAddress": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + }, + { + "address": "3JSf5tPeuscJGtaCp5giEiDhv51gQ4v3zWg8DGgyLfAB", + "symbol": "YFI", + "ethAddress": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e" + }, + { + "address": "CWE8jPTUYhdCTZYWPTe1o5DFqfdjzWKc9WKz6rSjQUdG", + "symbol": "LINK", + "ethAddress": "0x514910771af9ca656af840dff83e8264ecf986ca" + }, + { + "address": "AR1Mtgh7zAtxuxGd2XPovXPVjcSdY3i4rQYisNadjfKy", + "symbol": "SUSHI", + "ethAddress": "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2" + }, + { + "address": "CsZ5LZkDS7h9TDKjrbL7VAwQZ9nsRu8vJLhRYfmGaN8K", + "symbol": "ALEPH", + "ethAddress": "0x27702a26126e0B3702af63Ee09aC4d1A084EF628" + }, + { + "address": "SF3oTvfWzEP3DTwGSvUXRrGTvr75pdZNnBLAH9bzMuX", + "symbol": "SXP", + "ethAddress": "0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9" + }, + { + "address": "5Fu5UUgbjpUvdBveb3a1JTNirL8rXtiYeSMWvKjtUNQv", + "symbol": "CREAM", + "ethAddress": "0x2ba592F78dB6436527729929AAf6c908497cB200" + }, + { + "address": "9S4t2NEAiJVMvPdRYKVrfJpBafPBLtvbvyS3DecojQHw", + "symbol": "FRONT", + "ethAddress": "0xf8C3527CC04340b208C854E985240c02F7B7793f" + }, + { + "address": "6WNVCuxCGJzNjmMZoKyhZJwvJ5tYpsLyAtagzYASqBoF", + "symbol": "AKRO", + "ethAddress": "0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7" + }, + { + "address": "DJafV9qemGp7mLMEn5wrfqaFwxsbLgUsGVS16zKRk9kc", + "symbol": "HXRO", + "ethAddress": "0x4bd70556ae3f8a6ec6c4080a0c327b24325438f3" + }, + { + "address": "DEhAasscXF4kEGxFgJ3bq4PpVGp5wyUxMRvn6TzGVHaw", + "symbol": "UNI", + "ethAddress": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984" + }, + { + "address": "AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3", + "symbol": "FTT", + "ethAddress": "0x50d1c9771902476076ecfc8b2a83ad6b9355a4c9" + }, + { + "address": "EqWCKXfs3x47uVosDpTRgFniThL9Y8iCztJaapxbEaVX", + "symbol": "LUA", + "ethAddress": "0xb1f66997a5760428d3a87d68b90bfe0ae64121cc" + }, + { + "address": "GeDS162t9yGJuLEHPWXXGrb1zwkzinCgRwnT8vHYjKza", + "symbol": "MATH", + "ethAddress": "0x08d967bb0134f2d07f7cfb6e246680c53927dd30" + }, + { + "address": "GUohe4DJUA5FKPWo3joiPgsB7yzer7LpDmt1Vhzy3Zht", + "symbol": "KEEP", + "ethAddress": "0x85eee30c52b0b379b046fb0f85f4f3dc3009afec" + }, + { + "address": "9F9fNTT6qwjsu4X4yWYKZpsbw5qT7o6yR2i57JF2jagy", + "symbol": "SWAG", + "ethAddress": "0x87eDfFDe3E14c7a66c9b9724747a1C5696b742e6" + }, + { + "address": "DgHK9mfhMtUwwv54GChRrU54T2Em5cuszq2uMuen1ZVE", + "symbol": "CEL", + "ethAddress": "0xaaaebe6fe48e54f431b0c390cfaf0b017d09d42d" + }, + { + "address": "7ncCLJpP3MNww17LW8bRvx8odQQnubNtfNZBL5BgAEHW", + "symbol": "RSR", + "ethAddress": "0x8762db106b2c2a0bccb3a80d1ed41273552616e8" + }, + { + "address": "5wihEYGca7X4gSe97C5mVcqNsfxBzhdTwpv72HKs25US", + "symbol": "1INCH", + "ethAddress": "0x111111111117dc0aa78b770fa6a738034120c302" + }, + { + "address": "38i2NQxjp5rt5B3KogqrxmBxgrAwaB3W1f1GmiKqh9MS", + "symbol": "GRT", + "ethAddress": "0xc944e90c64b2c07662a292be6244bdf05cda44a7" + }, + { + "address": "Avz2fmevhhu87WYtWQCFj9UjKRjF9Z9QWwN2ih9yF95G", + "symbol": "COMP", + "ethAddress": "0xc00e94cb662c3520282e6f5717214004a7f26888" + }, + { + "address": "9wRD14AhdZ3qV8et3eBQVsrb3UoBZDUbJGyFckpTg8sj", + "symbol": "PAXG", + "ethAddress": "0x45804880De22913dAFE09f4980848ECE6EcbAf78" + } +] \ No newline at end of file diff --git a/solana/modules/token_bridge/program/src/api/tokens.txt b/solana/modules/token_bridge/program/src/api/tokens.txt new file mode 100644 index 000000000..9ed532b9d --- /dev/null +++ b/solana/modules/token_bridge/program/src/api/tokens.txt @@ -0,0 +1,44 @@ + // "WETH", + (2, b"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") => b"2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk", + // "YFI", + (2, b"0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e") => b"3JSf5tPeuscJGtaCp5giEiDhv51gQ4v3zWg8DGgyLfAB", + // "LINK", + (2, b"0x514910771af9ca656af840dff83e8264ecf986ca") => b"CWE8jPTUYhdCTZYWPTe1o5DFqfdjzWKc9WKz6rSjQUdG", + // "SUSHI", + (2, b"0x6b3595068778dd592e39a122f4f5a5cf09c90fe2") => b"AR1Mtgh7zAtxuxGd2XPovXPVjcSdY3i4rQYisNadjfKy", + // "ALEPH", + (2, b"0x27702a26126e0B3702af63Ee09aC4d1A084EF628") => b"CsZ5LZkDS7h9TDKjrbL7VAwQZ9nsRu8vJLhRYfmGaN8K", + // "SXP", + (2, b"0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9") => b"SF3oTvfWzEP3DTwGSvUXRrGTvr75pdZNnBLAH9bzMuX", + // "CREAM", + (2, b"0x2ba592F78dB6436527729929AAf6c908497cB200") => b"5Fu5UUgbjpUvdBveb3a1JTNirL8rXtiYeSMWvKjtUNQv", + // "FRONT", + (2, b"0xf8C3527CC04340b208C854E985240c02F7B7793f") => b"9S4t2NEAiJVMvPdRYKVrfJpBafPBLtvbvyS3DecojQHw", + // "AKRO", + (2, b"0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7") => b"6WNVCuxCGJzNjmMZoKyhZJwvJ5tYpsLyAtagzYASqBoF", + // "HXRO", + (2, b"0x4bd70556ae3f8a6ec6c4080a0c327b24325438f3") => b"DJafV9qemGp7mLMEn5wrfqaFwxsbLgUsGVS16zKRk9kc", + // "UNI", + (2, b"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984") => b"DEhAasscXF4kEGxFgJ3bq4PpVGp5wyUxMRvn6TzGVHaw", + // "FTT", + (2, b"0x50d1c9771902476076ecfc8b2a83ad6b9355a4c9") => b"AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3", + // "LUA", + (2, b"0xb1f66997a5760428d3a87d68b90bfe0ae64121cc") => b"EqWCKXfs3x47uVosDpTRgFniThL9Y8iCztJaapxbEaVX", + // "MATH", + (2, b"0x08d967bb0134f2d07f7cfb6e246680c53927dd30") => b"GeDS162t9yGJuLEHPWXXGrb1zwkzinCgRwnT8vHYjKza", + // "KEEP", + (2, b"0x85eee30c52b0b379b046fb0f85f4f3dc3009afec") => b"GUohe4DJUA5FKPWo3joiPgsB7yzer7LpDmt1Vhzy3Zht", + // "SWAG", + (2, b"0x87eDfFDe3E14c7a66c9b9724747a1C5696b742e6") => b"9F9fNTT6qwjsu4X4yWYKZpsbw5qT7o6yR2i57JF2jagy", + // "CEL", + (2, b"0xaaaebe6fe48e54f431b0c390cfaf0b017d09d42d") => b"DgHK9mfhMtUwwv54GChRrU54T2Em5cuszq2uMuen1ZVE", + // "RSR", + (2, b"0x8762db106b2c2a0bccb3a80d1ed41273552616e8") => b"7ncCLJpP3MNww17LW8bRvx8odQQnubNtfNZBL5BgAEHW", + // "1INCH", + (2, b"0x111111111117dc0aa78b770fa6a738034120c302") => b"5wihEYGca7X4gSe97C5mVcqNsfxBzhdTwpv72HKs25US", + // "GRT", + (2, b"0xc944e90c64b2c07662a292be6244bdf05cda44a7") => b"38i2NQxjp5rt5B3KogqrxmBxgrAwaB3W1f1GmiKqh9MS", + // "COMP", + (2, b"0xc00e94cb662c3520282e6f5717214004a7f26888") => b"Avz2fmevhhu87WYtWQCFj9UjKRjF9Z9QWwN2ih9yF95G", + // "PAXG", + (2, b"0x45804880De22913dAFE09f4980848ECE6EcbAf78") => b"9wRD14AhdZ3qV8et3eBQVsrb3UoBZDUbJGyFckpTg8sj", \ No newline at end of file diff --git a/solana/modules/token_bridge/program/src/api/transfer.rs b/solana/modules/token_bridge/program/src/api/transfer.rs index 63ca428aa..018322809 100644 --- a/solana/modules/token_bridge/program/src/api/transfer.rs +++ b/solana/modules/token_bridge/program/src/api/transfer.rs @@ -13,6 +13,7 @@ use crate::{ WrappedMint, WrappedTokenMeta, }, + api::derive_mint_for_token, messages::PayloadTransfer, types::*, TokenBridgeError, @@ -330,14 +331,34 @@ pub fn transfer_wrapped( invoke(&transfer_ix, ctx.accounts)?; + let (_, is_sollet) = derive_mint_for_token( + ctx.program_id, + accs.wrapped_meta.token_address, + accs.wrapped_meta.chain, + ); + let (amount, fee) = if is_sollet && accs.wrapped_meta.original_decimals > 6 { + // Sollet assets are truncated to 6 decimals, however Wormhole uses 8 and assumes + // wire-truncation to 8 decimals. + ( + data.amount + .checked_mul(10u64.pow(2.min(accs.wrapped_meta.original_decimals as u32 - 6))) + .unwrap(), + data.fee + .checked_mul(10u64.pow(2.min(accs.wrapped_meta.original_decimals as u32 - 6))) + .unwrap(), + ) + } else { + (data.amount, data.fee) + }; + // Post message let payload = PayloadTransfer { - amount: U256::from(data.amount), + amount: U256::from(amount), token_address: accs.wrapped_meta.token_address, token_chain: accs.wrapped_meta.chain, to: data.target_address, to_chain: data.target_chain, - fee: U256::from(data.fee), + fee: U256::from(fee), }; let params = ( bridge::instruction::Instruction::PostMessage, diff --git a/solana/modules/token_bridge/program/src/instructions.rs b/solana/modules/token_bridge/program/src/instructions.rs index 8ac6ed6db..3abb35e31 100644 --- a/solana/modules/token_bridge/program/src/instructions.rs +++ b/solana/modules/token_bridge/program/src/instructions.rs @@ -21,12 +21,14 @@ use crate::{ CompleteNativeData, CompleteWrappedData, }, + derive_mint_for_token, AttestTokenData, CreateWrappedData, RegisterChainData, TransferNativeData, TransferWrappedData, UpgradeContractData, + SOLLET_MINTS, }, messages::{ PayloadAssetMeta, @@ -169,13 +171,8 @@ pub fn complete_wrapped( }, &program_id, ); - let mint_key = WrappedMint::<'_, { AccountState::Uninitialized }>::key( - &WrappedDerivationData { - token_chain: payload.token_chain, - token_address: payload.token_address, - }, - &program_id, - ); + + let mint_key = derive_mint_for_token(&program_id, payload.token_address, payload.token_chain).0; let meta_key = WrappedTokenMeta::<'_, { AccountState::Uninitialized }>::key( &WrappedMetaDerivationData { mint_key }, &program_id, @@ -228,13 +225,7 @@ pub fn create_wrapped( }, &program_id, ); - let mint_key = WrappedMint::<'_, { AccountState::Uninitialized }>::key( - &WrappedDerivationData { - token_chain: payload.token_chain, - token_address: payload.token_address, - }, - &program_id, - ); + let mint_key = derive_mint_for_token(&program_id, payload.token_address, payload.token_chain).0; let mint_meta_key = WrappedTokenMeta::<'_, { AccountState::Uninitialized }>::key( &WrappedMetaDerivationData { mint_key }, &program_id, @@ -404,13 +395,7 @@ pub fn transfer_wrapped( ) -> solitaire::Result { let config_key = ConfigAccount::<'_, { AccountState::Uninitialized }>::key(None, &program_id); - let wrapped_mint_key = WrappedMint::<'_, { AccountState::Uninitialized }>::key( - &WrappedDerivationData { - token_chain, - token_address, - }, - &program_id, - ); + let wrapped_mint_key = derive_mint_for_token(&program_id, token_address, token_chain).0; let wrapped_meta_key = WrappedTokenMeta::<'_, { AccountState::Uninitialized }>::key( &WrappedMetaDerivationData { mint_key: wrapped_mint_key, diff --git a/solana/modules/token_bridge/program/src/types.rs b/solana/modules/token_bridge/program/src/types.rs index fa4c1005e..859c95520 100644 --- a/solana/modules/token_bridge/program/src/types.rs +++ b/solana/modules/token_bridge/program/src/types.rs @@ -50,6 +50,7 @@ impl Owned for EndpointRegistration { pub struct WrappedMeta { pub chain: ChainID, pub token_address: Address, + pub original_decimals: u8, } impl Owned for WrappedMeta { diff --git a/solana/modules/token_bridge/program/tests/integration.rs b/solana/modules/token_bridge/program/tests/integration.rs index 4b58775a2..d447fc1bf 100644 --- a/solana/modules/token_bridge/program/tests/integration.rs +++ b/solana/modules/token_bridge/program/tests/integration.rs @@ -94,9 +94,11 @@ use std::{ use token_bridge::{ accounts::{ EmitterAccount, + MintSigner, WrappedDerivationData, WrappedMint, }, + api::SOLLET_MINTS, messages::{ PayloadAssetMeta, PayloadGovernanceRegisterChain, @@ -271,6 +273,9 @@ fn run_integration_tests() { .unwrap(); test_transfer_wrapped_in(&mut context, wrapped_acc.pubkey()); test_transfer_wrapped(&mut context, wrapped_acc.pubkey()); + + // Sollet specific tests + test_create_wrapped_preexisting(&mut context); } fn test_attest(context: &mut Context) -> () { @@ -607,6 +612,84 @@ fn test_create_wrapped(context: &mut Context) -> (Pubkey) { ); } +fn test_create_wrapped_preexisting(context: &mut Context) -> (Pubkey) { + println!("CreateWrappedPreexisting"); + use token_bridge::{ + accounts::ConfigAccount, + types::Config, + }; + + let Context { + ref payer, + ref client, + ref bridge, + ref token_bridge, + ref mint_authority, + ref mint, + ref mint_meta, + ref token_account, + ref token_authority, + .. + } = context; + + // FDhdMYh3KsF64Jxzh8tnx9rJXQTcN461rguUK9z9zm64 + let mint_keypair = Keypair::from_bytes(&[ + 78, 244, 23, 240, 92, 61, 31, 184, 188, 176, 28, 188, 143, 230, 185, 139, 23, 32, 60, 221, + 166, 209, 15, 175, 243, 160, 174, 226, 190, 8, 124, 115, 211, 68, 134, 6, 252, 30, 9, 108, + 54, 236, 74, 254, 5, 8, 178, 146, 14, 182, 243, 214, 1, 108, 184, 93, 66, 224, 100, 135, + 16, 120, 69, 93, + ]) + .unwrap(); + // Create a wrapped mint + common::create_mint( + client, + payer, + &MintSigner::key(None, token_bridge), + &mint_keypair, + ); + + let nonce = rand::thread_rng().gen(); + println!("{}", hex::encode([0xaau8; 32])); + println!("{:?}", SOLLET_MINTS); + println!("{:?}", SOLLET_MINTS.get(hex::encode([0xaau8; 32]).as_str())); + + let payload = PayloadAssetMeta { + token_address: [0xaau8; 32], + token_chain: 2, + decimals: 7, + symbol: "".to_string(), + name: "".to_string(), + }; + let message = payload.try_to_vec().unwrap(); + + let (vaa, _, _) = common::generate_vaa([0u8; 32], 2, message, nonce, 120); + common::post_vaa(client, bridge, payer, vaa.clone()).unwrap(); + let mut msg_derivation_data = &PostedVAADerivationData { + payload_hash: bridge::instructions::hash_vaa(&vaa).to_vec(), + }; + let message_key = + PostedVAA::<'_, { AccountState::MaybeInitialized }>::key(&msg_derivation_data, &bridge); + + common::create_wrapped( + client, + token_bridge, + bridge, + &message_key, + vaa, + payload, + payer, + ) + .unwrap(); + + return WrappedMint::<'_, { AccountState::Initialized }>::key( + &WrappedDerivationData { + token_chain: 2, + token_address: [0xaau8; 32], + }, + token_bridge, + ); +} + fn test_initialize(context: &mut Context) { println!("Initialize"); use token_bridge::{