Build token migration program

Change-Id: I78b10931fb09f5bca457c1fb36095fdbcea9e65f
This commit is contained in:
Hendrik Hofstadt 2021-09-09 22:09:01 +02:00
parent c116b3c813
commit 88933d13e2
16 changed files with 1125 additions and 0 deletions

View File

@ -59,6 +59,9 @@ spec:
- --bpf-program
- P2WH424242424242424242424242424242424242424
- /opt/solana/deps/pyth2wormhole.so
- --bpf-program
- Ex9bCdVMSfx7EzB3pgSi2R4UHwJAXvTw18rBQm5YQ8gK
- /opt/solana/deps/wormhole_migration.so
- --log
ports:
- containerPort: 8001

View File

@ -14,6 +14,7 @@
| Bridge Core | SOL | Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o | |
| Token Bridge | SOL | B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE | |
| NFT Bridge | SOL | NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA | |
| Migration Contract | SOL | Ex9bCdVMSfx7EzB3pgSi2R4UHwJAXvTw18rBQm5YQ8gK | |
| Test Wallet | Terra | terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v | Mnemonic: `notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius` |
| Example Token | Terra | terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh | Tokens minted to Test Wallet |
| Bridge Core | Terra | terra18eezxhys9jwku67cm4w84xhnzt4xjj77w2qt62 | |

View File

@ -48,13 +48,16 @@ RUN --mount=type=cache,target=bridge/target \
--mount=type=cache,target=modules/token_bridge/target \
--mount=type=cache,target=modules/nft_bridge/target \
--mount=type=cache,target=pyth2wormhole/target \
--mount=type=cache,target=migration/target \
cargo build-bpf --manifest-path "bridge/program/Cargo.toml" && \
cargo build-bpf --manifest-path "bridge/cpi_poster/Cargo.toml" && \
cargo build-bpf --manifest-path "modules/token_bridge/program/Cargo.toml" && \
cargo build-bpf --manifest-path "pyth2wormhole/program/Cargo.toml" && \
cargo build-bpf --manifest-path "modules/nft_bridge/program/Cargo.toml" && \
cargo build-bpf --manifest-path "migration/Cargo.toml" && \
cp bridge/target/deploy/bridge.so /opt/solana/deps/bridge.so && \
cp bridge/target/deploy/cpi_poster.so /opt/solana/deps/cpi_poster.so && \
cp migration/target/deploy/wormhole_migration.so /opt/solana/deps/wormhole_migration.so && \
cp modules/token_bridge/target/deploy/token_bridge.so /opt/solana/deps/token_bridge.so && \
cp modules/nft_bridge/target/deploy/nft_bridge.so /opt/solana/deps/nft_bridge.so && \
cp modules/token_bridge/token-metadata/spl_token_metadata.so /opt/solana/deps/spl_token_metadata.so && \

View File

@ -16,6 +16,7 @@ ENV BRIDGE_ADDRESS="Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
COPY bridge bridge
COPY modules modules
COPY solitaire solitaire
COPY migration migration
# Compile Wormhole
RUN --mount=type=cache,target=/root/.cache \
@ -35,6 +36,11 @@ RUN --mount=type=cache,target=/root/.cache \
--mount=type=cache,target=modules/token_bridge/target \
cd modules/token_bridge/program && /usr/local/cargo/bin/wasm-pack build --target nodejs -d nodejs -- --features wasm
# Compile Migration
RUN --mount=type=cache,target=/root/.cache \
--mount=type=cache,target=migration/target \
cd migration && /usr/local/cargo/bin/wasm-pack build --target bundler -d bundler -- --features wasm
# Compile NFT Bridge
RUN --mount=type=cache,target=/root/.cache \
--mount=type=cache,target=modules/nft_bridge/target \
@ -48,6 +54,7 @@ FROM scratch AS export
COPY --from=build /usr/src/bridge/bridge/program/bundler sdk/js/src/solana/core
COPY --from=build /usr/src/bridge/modules/token_bridge/program/bundler sdk/js/src/solana/token
COPY --from=build /usr/src/bridge/migration/bundler sdk/js/src/solana/migration
COPY --from=build /usr/src/bridge/modules/nft_bridge/program/bundler sdk/js/src/solana/nft
COPY --from=build /usr/src/bridge/bridge/program/nodejs clients/solana/pkg

View File

@ -0,0 +1,41 @@
[package]
name = "wormhole-migration"
version = "0.1.0"
description = "Created with Rocksalt"
edition = "2018"
[lib]
crate-type = ["cdylib", "lib"]
name = "wormhole_migration"
[features]
no-entrypoint = ["solitaire/no-entrypoint", "rand"]
trace = ["solitaire/trace"]
wasm = ["no-entrypoint"]
client = ["solitaire-client", "solitaire/client", "no-entrypoint"]
cpi = ["no-entrypoint"]
default = []
[dependencies]
borsh = "0.8.1"
byteorder = "1.4.3"
rocksalt = { path = "../solitaire/rocksalt" }
solitaire = { path = "../solitaire/program" }
sha3 = "0.9.1"
solana-program = "*"
spl-token = { version = "=3.1.0", features = ["no-entrypoint"] }
solitaire-client = { path = "../solitaire/client", optional = true }
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] }
serde = { version = "1.0", features = ["derive"] }
rand = { version = "0.7.3", optional = true }
[dev-dependencies]
hex = "*"
hex-literal = "0.3.1"
libsecp256k1 = { version = "0.3.5", features = [] }
solana-client = "1.7.0"
solana-sdk = "=1.7.0"
spl-token = { version = "=3.1.0", features = ["no-entrypoint"] }
[patch.crates-io]
memmap2 = { path = "../bridge/memmap2-rs" }

View File

@ -0,0 +1,11 @@
# Merge similar crates together to avoid multiple use statements.
imports_granularity = "Crate"
# Consistency in formatting makes tool based searching/editing better.
empty_item_single_line = false
# Easier editing when arbitrary mixed use statements do not collapse.
imports_layout = "Vertical"
# Default rustfmt formatting of match arms with branches is awful.
match_arm_leading_pipes = "Preserve"

View File

@ -0,0 +1,84 @@
use crate::types::{
PoolData,
SplAccount,
SplMint,
};
use solana_program::pubkey::Pubkey;
use solitaire::{
processors::seeded::Seeded,
AccountState,
Data,
Derive,
Info,
};
pub type ShareMint<'a, const STATE: AccountState> = Data<'a, SplMint, { STATE }>;
pub struct ShareMintDerivationData {
pub pool: Pubkey,
}
impl<'b, const STATE: AccountState> Seeded<&ShareMintDerivationData> for ShareMint<'b, { STATE }> {
fn seeds(accs: &ShareMintDerivationData) -> Vec<Vec<u8>> {
vec![
String::from("share_mint").as_bytes().to_vec(),
accs.pool.to_bytes().to_vec(),
]
}
}
pub type FromCustodyTokenAccount<'a, const STATE: AccountState> = Data<'a, SplAccount, { STATE }>;
pub struct FromCustodyTokenAccountDerivationData {
pub pool: Pubkey,
}
impl<'b, const STATE: AccountState> Seeded<&FromCustodyTokenAccountDerivationData>
for FromCustodyTokenAccount<'b, { STATE }>
{
fn seeds(accs: &FromCustodyTokenAccountDerivationData) -> Vec<Vec<u8>> {
vec![
String::from("from_custody").as_bytes().to_vec(),
accs.pool.to_bytes().to_vec(),
]
}
}
pub type ToCustodyTokenAccount<'a, const STATE: AccountState> = Data<'a, SplAccount, { STATE }>;
pub struct ToCustodyTokenAccountDerivationData {
pub pool: Pubkey,
}
impl<'b, const STATE: AccountState> Seeded<&ToCustodyTokenAccountDerivationData>
for ToCustodyTokenAccount<'b, { STATE }>
{
fn seeds(accs: &ToCustodyTokenAccountDerivationData) -> Vec<Vec<u8>> {
vec![
String::from("to_custody").as_bytes().to_vec(),
accs.pool.to_bytes().to_vec(),
]
}
}
pub type MigrationPool<'a, const STATE: AccountState> = Data<'a, PoolData, { STATE }>;
pub struct MigrationPoolDerivationData {
pub from: Pubkey,
pub to: Pubkey,
}
impl<'b, const STATE: AccountState> Seeded<&MigrationPoolDerivationData>
for MigrationPool<'b, { STATE }>
{
fn seeds(accs: &MigrationPoolDerivationData) -> Vec<Vec<u8>> {
vec![
String::from("pool").as_bytes().to_vec(),
accs.from.to_bytes().to_vec(),
accs.to.to_bytes().to_vec(),
]
}
}
pub type CustodySigner<'a> = Derive<Info<'a>, "custody_signer">;
pub type AuthoritySigner<'a> = Derive<Info<'a>, "authority_signer">;

View File

@ -0,0 +1,4 @@
pub mod add_liquidity;
pub mod claim_shares;
pub mod create_pool;
pub mod migrate_tokens;

View File

@ -0,0 +1,112 @@
use crate::{
accounts::{
AuthoritySigner,
CustodySigner,
MigrationPool,
ShareMint,
ShareMintDerivationData,
ToCustodyTokenAccount,
ToCustodyTokenAccountDerivationData,
},
types::{
SplAccount,
SplMint,
},
MigrationError::WrongMint,
};
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use crate::accounts::MigrationPoolDerivationData;
use solitaire::*;
#[derive(FromAccounts)]
pub struct AddLiquidity<'b> {
pub pool: Mut<MigrationPool<'b, { AccountState::Initialized }>>,
pub from_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub to_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub to_token_custody: Mut<ToCustodyTokenAccount<'b, { AccountState::Initialized }>>,
pub share_mint: Mut<ShareMint<'b, { AccountState::Initialized }>>,
pub to_lp_acc: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
pub lp_share_acc: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
pub custody_signer: CustodySigner<'b>,
pub authority_signer: AuthoritySigner<'b>,
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct AddLiquidityData {
pub amount: u64,
}
pub fn add_liquidity(
ctx: &ExecutionContext,
accs: &mut AddLiquidity,
data: AddLiquidityData,
) -> Result<()> {
if *accs.from_mint.info().key != accs.pool.from {
return Err(WrongMint.into());
}
if *accs.to_mint.info().key != accs.pool.to {
return Err(WrongMint.into());
}
if accs.lp_share_acc.mint != *accs.share_mint.info().key {
return Err(WrongMint.into());
}
accs.to_token_custody.verify_derivation(
ctx.program_id,
&ToCustodyTokenAccountDerivationData {
pool: *accs.pool.info().key,
},
)?;
accs.share_mint.verify_derivation(
ctx.program_id,
&ShareMintDerivationData {
pool: *accs.pool.info().key,
},
)?;
accs.pool.verify_derivation(
ctx.program_id,
&MigrationPoolDerivationData {
from: accs.pool.from,
to: accs.pool.to,
},
)?;
// Transfer out-tokens in
let transfer_ix = spl_token::instruction::transfer(
&spl_token::id(),
accs.to_lp_acc.info().key,
accs.to_token_custody.info().key,
accs.authority_signer.key,
&[],
data.amount,
)?;
invoke_seeded(&transfer_ix, ctx, &accs.authority_signer, None)?;
// The share amount should be equal to the amount of from tokens an lp would be getting
let share_amount = if accs.from_mint.decimals > accs.to_mint.decimals {
data.amount
.checked_mul(10u64.pow((accs.from_mint.decimals - accs.to_mint.decimals) as u32))
.unwrap()
} else {
data.amount
.checked_div(10u64.pow((accs.to_mint.decimals - accs.from_mint.decimals) as u32))
.unwrap()
};
// Mint LP shares
let mint_ix = spl_token::instruction::mint_to(
&spl_token::id(),
accs.from_mint.info().key,
accs.lp_share_acc.info().key,
accs.custody_signer.key,
&[],
share_amount,
)?;
invoke_seeded(&mint_ix, ctx, &accs.custody_signer, None)?;
Ok(())
}

View File

@ -0,0 +1,103 @@
use crate::{
accounts::{
AuthoritySigner,
CustodySigner,
FromCustodyTokenAccountDerivationData,
MigrationPool,
ShareMint,
ShareMintDerivationData,
ToCustodyTokenAccount,
},
types::{
SplAccount,
SplMint,
},
MigrationError::WrongMint,
};
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use crate::accounts::MigrationPoolDerivationData;
use solitaire::{
processors::seeded::{
invoke_seeded,
Seeded,
},
*,
};
#[derive(FromAccounts)]
pub struct ClaimShares<'b> {
pub pool: Mut<MigrationPool<'b, { AccountState::Initialized }>>,
pub to_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub from_token_custody: Mut<ToCustodyTokenAccount<'b, { AccountState::Initialized }>>,
pub share_mint: Mut<ShareMint<'b, { AccountState::Initialized }>>,
pub from_lp_acc: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
pub lp_share_acc: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
pub custody_signer: CustodySigner<'b>,
pub authority_signer: AuthoritySigner<'b>,
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct ClaimSharesData {
pub amount: u64,
}
pub fn claim_shares(
ctx: &ExecutionContext,
accs: &mut ClaimShares,
data: ClaimSharesData,
) -> Result<()> {
if *accs.to_mint.info().key != accs.pool.to {
return Err(WrongMint.into());
}
if accs.lp_share_acc.mint != *accs.share_mint.info().key {
return Err(WrongMint.into());
}
accs.from_token_custody.verify_derivation(
ctx.program_id,
&FromCustodyTokenAccountDerivationData {
pool: *accs.pool.info().key,
},
)?;
accs.share_mint.verify_derivation(
ctx.program_id,
&ShareMintDerivationData {
pool: *accs.pool.info().key,
},
)?;
accs.pool.verify_derivation(
ctx.program_id,
&MigrationPoolDerivationData {
from: accs.pool.from,
to: accs.pool.to,
},
)?;
// Transfer claimed tokens to LP
let transfer_ix = spl_token::instruction::transfer(
&spl_token::id(),
accs.from_token_custody.info().key,
accs.from_lp_acc.info().key,
accs.custody_signer.key,
&[],
data.amount,
)?;
invoke_seeded(&transfer_ix, ctx, &accs.custody_signer, None)?;
// Burn LP shares
let mint_ix = spl_token::instruction::burn(
&spl_token::id(),
accs.lp_share_acc.info().key,
accs.share_mint.info().key,
accs.authority_signer.key,
&[],
data.amount,
)?;
invoke_seeded(&mint_ix, ctx, &accs.authority_signer, None)?;
Ok(())
}

View File

@ -0,0 +1,119 @@
use crate::{
accounts::{
CustodySigner,
FromCustodyTokenAccount,
FromCustodyTokenAccountDerivationData,
MigrationPool,
MigrationPoolDerivationData,
ShareMint,
ShareMintDerivationData,
ToCustodyTokenAccount,
ToCustodyTokenAccountDerivationData,
},
types::SplMint,
};
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use solana_program::program::invoke_signed;
use solitaire::{
processors::seeded::Seeded,
CreationLamports::Exempt,
*,
};
#[derive(FromAccounts)]
pub struct CreatePool<'b> {
pub payer: Mut<Signer<Info<'b>>>,
pub pool: Mut<MigrationPool<'b, { AccountState::Uninitialized }>>,
pub from_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub to_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub from_token_custody: Mut<FromCustodyTokenAccount<'b, { AccountState::Uninitialized }>>,
pub to_token_custody: Mut<ToCustodyTokenAccount<'b, { AccountState::Uninitialized }>>,
pub pool_mint: Mut<ShareMint<'b, { AccountState::Uninitialized }>>,
pub custody_signer: CustodySigner<'b>,
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct CreatePoolData {}
pub fn create_pool(
ctx: &ExecutionContext,
accs: &mut CreatePool,
_data: CreatePoolData,
) -> Result<()> {
// Create from custody account
accs.from_token_custody.create(
&FromCustodyTokenAccountDerivationData {
pool: *accs.pool.info().key,
},
ctx,
accs.payer.key,
Exempt,
)?;
let init_ix = spl_token::instruction::initialize_account(
&spl_token::id(),
accs.from_token_custody.info().key,
accs.from_mint.info().key,
accs.custody_signer.info().key,
)?;
invoke_signed(&init_ix, ctx.accounts, &[])?;
// Create to custody account
accs.to_token_custody.create(
&ToCustodyTokenAccountDerivationData {
pool: *accs.pool.info().key,
},
ctx,
accs.payer.key,
Exempt,
)?;
let init_ix = spl_token::instruction::initialize_account(
&spl_token::id(),
accs.to_token_custody.info().key,
accs.to_mint.info().key,
accs.custody_signer.info().key,
)?;
invoke_signed(&init_ix, ctx.accounts, &[])?;
// Create to pool mint
accs.pool_mint.create(
&ShareMintDerivationData {
pool: *accs.pool.info().key,
},
ctx,
accs.payer.key,
Exempt,
)?;
let init_ix = spl_token::instruction::initialize_mint(
&spl_token::id(),
accs.pool_mint.info().key,
accs.custody_signer.info().key,
None,
accs.from_mint.decimals,
)?;
invoke_signed(&init_ix, ctx.accounts, &[])?;
// Set fields on pool
accs.pool.from = *accs.from_mint.info().key;
accs.pool.to = *accs.to_mint.info().key;
// Create pool
accs.pool.create(
&MigrationPoolDerivationData {
from: *accs.from_mint.info().key,
to: *accs.to_mint.info().key,
},
ctx,
accs.payer.key,
Exempt,
)?;
Ok(())
}

View File

@ -0,0 +1,121 @@
use crate::{
accounts::{
AuthoritySigner,
CustodySigner,
FromCustodyTokenAccount,
FromCustodyTokenAccountDerivationData,
MigrationPool,
ToCustodyTokenAccount,
ToCustodyTokenAccountDerivationData,
},
types::{
SplAccount,
SplMint,
},
MigrationError::WrongMint,
};
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use crate::accounts::MigrationPoolDerivationData;
use solitaire::{
processors::seeded::{
invoke_seeded,
Seeded,
},
*,
};
#[derive(FromAccounts)]
pub struct MigrateTokens<'b> {
pub pool: Mut<MigrationPool<'b, { AccountState::Initialized }>>,
pub from_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub to_mint: Data<'b, SplMint, { AccountState::Initialized }>,
pub to_token_custody: Mut<ToCustodyTokenAccount<'b, { AccountState::Initialized }>>,
pub from_token_custody: Mut<FromCustodyTokenAccount<'b, { AccountState::Initialized }>>,
pub user_from_acc: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
pub user_to_acc: Mut<Data<'b, SplAccount, { AccountState::Initialized }>>,
pub custody_signer: CustodySigner<'b>,
pub authority_signer: AuthoritySigner<'b>,
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct MigrateTokensData {
pub amount: u64,
}
pub fn migrate_tokens(
ctx: &ExecutionContext,
accs: &mut MigrateTokens,
data: MigrateTokensData,
) -> Result<()> {
if *accs.from_mint.info().key != accs.pool.from {
return Err(WrongMint.into());
}
if *accs.to_mint.info().key != accs.pool.to {
return Err(WrongMint.into());
}
if accs.user_from_acc.mint != accs.pool.from {
return Err(WrongMint.into());
}
if accs.user_to_acc.mint != accs.pool.to {
return Err(WrongMint.into());
}
accs.to_token_custody.verify_derivation(
ctx.program_id,
&ToCustodyTokenAccountDerivationData {
pool: *accs.pool.info().key,
},
)?;
accs.from_token_custody.verify_derivation(
ctx.program_id,
&FromCustodyTokenAccountDerivationData {
pool: *accs.pool.info().key,
},
)?;
accs.pool.verify_derivation(
ctx.program_id,
&MigrationPoolDerivationData {
from: accs.pool.from,
to: accs.pool.to,
},
)?;
// Transfer in-tokens in
let transfer_ix = spl_token::instruction::transfer(
&spl_token::id(),
accs.user_from_acc.info().key,
accs.from_token_custody.info().key,
accs.authority_signer.key,
&[],
data.amount,
)?;
invoke_seeded(&transfer_ix, ctx, &accs.authority_signer, None)?;
// The share amount should be equal to the amount of from tokens an lp would be getting
let out_amount = if accs.from_mint.decimals > accs.to_mint.decimals {
data.amount
.checked_div(10u64.pow((accs.from_mint.decimals - accs.to_mint.decimals) as u32))
.unwrap()
} else {
data.amount
.checked_mul(10u64.pow((accs.to_mint.decimals - accs.from_mint.decimals) as u32))
.unwrap()
};
// Transfer out-tokens to user
let transfer_ix = spl_token::instruction::transfer(
&spl_token::id(),
accs.to_token_custody.info().key,
accs.user_to_acc.info().key,
accs.custody_signer.key,
&[],
out_amount,
)?;
invoke_seeded(&transfer_ix, ctx, &accs.custody_signer, None)?;
Ok(())
}

View File

@ -0,0 +1,242 @@
use crate::{
accounts::{
AuthoritySigner,
CustodySigner,
FromCustodyTokenAccount,
FromCustodyTokenAccountDerivationData,
MigrationPool,
MigrationPoolDerivationData,
ShareMint,
ShareMintDerivationData,
ToCustodyTokenAccount,
ToCustodyTokenAccountDerivationData,
},
api::{
add_liquidity::AddLiquidityData,
claim_shares::ClaimSharesData,
create_pool::CreatePoolData,
migrate_tokens::MigrateTokensData,
},
};
use borsh::BorshSerialize;
use solana_program::{
instruction::{
AccountMeta,
Instruction,
},
pubkey::Pubkey,
};
use solitaire::{
processors::seeded::Seeded,
AccountState,
};
pub fn add_liquidity(
program_id: Pubkey,
from_mint: Pubkey,
to_mint: Pubkey,
liquidity_token_account: Pubkey,
lp_share_token_account: Pubkey,
amount: u64,
) -> solitaire::Result<Instruction> {
let pool = MigrationPool::<'_, { AccountState::Initialized }>::key(
&MigrationPoolDerivationData {
from: from_mint,
to: to_mint,
},
&program_id,
);
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(pool, false),
AccountMeta::new_readonly(from_mint, false),
AccountMeta::new_readonly(to_mint, false),
AccountMeta::new(
ToCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key(
&ToCustodyTokenAccountDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(
ShareMint::<'_, { AccountState::Uninitialized }>::key(
&ShareMintDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(liquidity_token_account, false),
AccountMeta::new(lp_share_token_account, false),
AccountMeta::new_readonly(CustodySigner::key(None, &program_id), false),
AccountMeta::new_readonly(AuthoritySigner::key(None, &program_id), false),
// Dependencies
AccountMeta::new(solana_program::sysvar::rent::id(), false),
AccountMeta::new(solana_program::system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: (
crate::instruction::Instruction::AddLiquidity,
AddLiquidityData { amount },
)
.try_to_vec()?,
})
}
pub fn claim_shares(
program_id: Pubkey,
from_mint: Pubkey,
to_mint: Pubkey,
output_token_account: Pubkey,
lp_share_token_account: Pubkey,
amount: u64,
) -> solitaire::Result<Instruction> {
let pool = MigrationPool::<'_, { AccountState::Initialized }>::key(
&MigrationPoolDerivationData {
from: from_mint,
to: to_mint,
},
&program_id,
);
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(pool, false),
AccountMeta::new_readonly(to_mint, false),
AccountMeta::new(
FromCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key(
&FromCustodyTokenAccountDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(
ShareMint::<'_, { AccountState::Uninitialized }>::key(
&ShareMintDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(output_token_account, false),
AccountMeta::new(lp_share_token_account, false),
AccountMeta::new_readonly(CustodySigner::key(None, &program_id), false),
AccountMeta::new_readonly(AuthoritySigner::key(None, &program_id), false),
// Dependencies
AccountMeta::new(solana_program::sysvar::rent::id(), false),
AccountMeta::new(solana_program::system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: (
crate::instruction::Instruction::ClaimShares,
ClaimSharesData { amount },
)
.try_to_vec()?,
})
}
pub fn create_pool(
program_id: Pubkey,
payer: Pubkey,
from_mint: Pubkey,
to_mint: Pubkey,
) -> solitaire::Result<Instruction> {
let pool = MigrationPool::<'_, { AccountState::Initialized }>::key(
&MigrationPoolDerivationData {
from: from_mint,
to: to_mint,
},
&program_id,
);
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(pool, false),
AccountMeta::new_readonly(from_mint, false),
AccountMeta::new_readonly(to_mint, false),
AccountMeta::new(
FromCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key(
&FromCustodyTokenAccountDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(
ToCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key(
&ToCustodyTokenAccountDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(
ShareMint::<'_, { AccountState::Uninitialized }>::key(
&ShareMintDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new_readonly(CustodySigner::key(None, &program_id), false),
// Dependencies
AccountMeta::new(solana_program::sysvar::rent::id(), false),
AccountMeta::new(solana_program::system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: (
crate::instruction::Instruction::CreatePool,
CreatePoolData {},
)
.try_to_vec()?,
})
}
pub fn migrate_tokens(
program_id: Pubkey,
from_mint: Pubkey,
to_mint: Pubkey,
input_token_account: Pubkey,
output_token_account: Pubkey,
amount: u64,
) -> solitaire::Result<Instruction> {
let pool = MigrationPool::<'_, { AccountState::Initialized }>::key(
&MigrationPoolDerivationData {
from: from_mint,
to: to_mint,
},
&program_id,
);
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(pool, false),
AccountMeta::new_readonly(from_mint, false),
AccountMeta::new_readonly(to_mint, false),
AccountMeta::new(
ToCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key(
&ToCustodyTokenAccountDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(
FromCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key(
&FromCustodyTokenAccountDerivationData { pool },
&program_id,
),
false,
),
AccountMeta::new(input_token_account, false),
AccountMeta::new(output_token_account, false),
AccountMeta::new_readonly(CustodySigner::key(None, &program_id), false),
AccountMeta::new_readonly(AuthoritySigner::key(None, &program_id), false),
// Dependencies
AccountMeta::new(solana_program::sysvar::rent::id(), false),
AccountMeta::new(solana_program::system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: (
crate::instruction::Instruction::MigrateTokens,
MigrateTokensData { amount },
)
.try_to_vec()?,
})
}

View File

@ -0,0 +1,45 @@
#![allow(incomplete_features)]
#![feature(const_generics)]
use api::{
add_liquidity::*,
claim_shares::*,
create_pool::*,
migrate_tokens::*,
};
use solitaire::{
solitaire,
SolitaireError,
};
pub mod accounts;
pub mod api;
pub mod types;
#[cfg(feature = "no-entrypoint")]
pub mod instructions;
#[cfg(feature = "wasm")]
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
extern crate wasm_bindgen;
#[cfg(feature = "wasm")]
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub mod wasm;
pub enum MigrationError {
WrongMint,
}
impl From<MigrationError> for SolitaireError {
fn from(t: MigrationError) -> SolitaireError {
SolitaireError::Custom(t as u64)
}
}
solitaire! {
AddLiquidity(AddLiquidityData) => add_liquidity,
ClaimShares(ClaimSharesData) => claim_shares,
CreatePool(CreatePoolData) => create_pool,
MigrateTokens(MigrateTokensData) => migrate_tokens,
}

View File

@ -0,0 +1,35 @@
use borsh::{
BorshDeserialize,
BorshSerialize,
};
use serde::{
Deserialize,
Serialize,
};
use solana_program::pubkey::Pubkey;
use solitaire::{
pack_type,
processors::seeded::{
AccountOwner,
Owned,
},
};
use spl_token::state::{
Account,
Mint,
};
#[derive(Default, Clone, Copy, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
pub struct PoolData {
pub from: Pubkey,
pub to: Pubkey,
}
impl Owned for PoolData {
fn owner(&self) -> AccountOwner {
AccountOwner::This
}
}
pack_type!(SplMint, Mint, AccountOwner::Other(spl_token::id()));
pack_type!(SplAccount, Account, AccountOwner::Other(spl_token::id()));

View File

@ -0,0 +1,194 @@
use crate::{
accounts::{
AuthoritySigner,
FromCustodyTokenAccount,
FromCustodyTokenAccountDerivationData,
MigrationPool,
MigrationPoolDerivationData,
ShareMint,
ShareMintDerivationData,
ToCustodyTokenAccount,
ToCustodyTokenAccountDerivationData,
},
instructions,
types::PoolData,
};
use borsh::BorshDeserialize;
use solana_program::pubkey::Pubkey;
use solitaire::{
processors::seeded::Seeded,
AccountState,
};
use std::str::FromStr;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add_liquidity(
program_id: String,
from_mint: String,
to_mint: String,
liquidity_token_account: String,
lp_share_token_account: String,
amount: u64,
) -> JsValue {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let from_mint = Pubkey::from_str(from_mint.as_str()).unwrap();
let to_mint = Pubkey::from_str(to_mint.as_str()).unwrap();
let liquidity_token_account = Pubkey::from_str(liquidity_token_account.as_str()).unwrap();
let lp_share_token_account = Pubkey::from_str(lp_share_token_account.as_str()).unwrap();
let ix = instructions::add_liquidity(
program_id,
from_mint,
to_mint,
liquidity_token_account,
lp_share_token_account,
amount,
)
.unwrap();
JsValue::from_serde(&ix).unwrap()
}
#[wasm_bindgen]
pub fn claim_shares(
program_id: String,
from_mint: String,
to_mint: String,
output_token_account: String,
lp_share_token_account: String,
amount: u64,
) -> JsValue {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let from_mint = Pubkey::from_str(from_mint.as_str()).unwrap();
let to_mint = Pubkey::from_str(to_mint.as_str()).unwrap();
let output_token_account = Pubkey::from_str(output_token_account.as_str()).unwrap();
let lp_share_token_account = Pubkey::from_str(lp_share_token_account.as_str()).unwrap();
let ix = instructions::claim_shares(
program_id,
from_mint,
to_mint,
output_token_account,
lp_share_token_account,
amount,
)
.unwrap();
JsValue::from_serde(&ix).unwrap()
}
#[wasm_bindgen]
pub fn create_pool(
program_id: String,
payer: String,
from_mint: String,
to_mint: String,
) -> JsValue {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let payer = Pubkey::from_str(payer.as_str()).unwrap();
let from_mint = Pubkey::from_str(from_mint.as_str()).unwrap();
let to_mint = Pubkey::from_str(to_mint.as_str()).unwrap();
let ix = instructions::create_pool(program_id, payer, from_mint, to_mint).unwrap();
JsValue::from_serde(&ix).unwrap()
}
#[wasm_bindgen]
pub fn migrate_tokens(
program_id: String,
from_mint: String,
to_mint: String,
input_token_account: String,
output_token_account: String,
amount: u64,
) -> JsValue {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let from_mint = Pubkey::from_str(from_mint.as_str()).unwrap();
let to_mint = Pubkey::from_str(to_mint.as_str()).unwrap();
let input_token_account = Pubkey::from_str(input_token_account.as_str()).unwrap();
let output_token_account = Pubkey::from_str(output_token_account.as_str()).unwrap();
let ix = instructions::migrate_tokens(
program_id,
from_mint,
to_mint,
input_token_account,
output_token_account,
amount,
)
.unwrap();
JsValue::from_serde(&ix).unwrap()
}
#[wasm_bindgen]
pub fn pool_address(program_id: String, from_mint: String, to_mint: String) -> Vec<u8> {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let from_mint_key = Pubkey::from_str(from_mint.as_str()).unwrap();
let to_mint_key = Pubkey::from_str(to_mint.as_str()).unwrap();
let pool_addr = MigrationPool::<'_, { AccountState::Initialized }>::key(
&MigrationPoolDerivationData {
from: from_mint_key,
to: to_mint_key,
},
&program_id,
);
pool_addr.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn authority_address(program_id: String) -> Vec<u8> {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let authority_addr = AuthoritySigner::key(None, &program_id);
authority_addr.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn share_mint_address(program_id: String, pool: String) -> Vec<u8> {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let pool_key = Pubkey::from_str(pool.as_str()).unwrap();
let share_mint_addr = ShareMint::<'_, { AccountState::Initialized }>::key(
&ShareMintDerivationData { pool: pool_key },
&program_id,
);
share_mint_addr.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn from_custody_address(program_id: String, pool: String) -> Vec<u8> {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let pool_key = Pubkey::from_str(pool.as_str()).unwrap();
let from_custody_addr = FromCustodyTokenAccount::<'_, { AccountState::Initialized }>::key(
&FromCustodyTokenAccountDerivationData { pool: pool_key },
&program_id,
);
from_custody_addr.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn to_custody_address(program_id: String, pool: String) -> Vec<u8> {
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
let pool_key = Pubkey::from_str(pool.as_str()).unwrap();
let to_custody_addr = ToCustodyTokenAccount::<'_, { AccountState::Initialized }>::key(
&ToCustodyTokenAccountDerivationData { pool: pool_key },
&program_id,
);
to_custody_addr.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn parse_pool(data: Vec<u8>) -> JsValue {
JsValue::from_serde(&PoolData::try_from_slice(data.as_slice()).unwrap()).unwrap()
}