-
Notifications
You must be signed in to change notification settings - Fork 30
feat(solana): deposit command #317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
b53146d
feat(solana): deposit command
fadeev 7c96815
refactor
fadeev 4c27cc1
both localnet and devnet work
fadeev 428b221
--network flag
fadeev 84f6eee
lint
fadeev 2b4478a
wip
fadeev e769aca
wip
fadeev 8acea6e
fix build
fadeev 24e7a4a
fix build
fadeev ab88103
remove from
fadeev 89b2fc1
derive to and from
fadeev 0f3d1f8
lint
fadeev fc1b6e8
default token address
fadeev 1aec56c
merge main
fadeev f928096
fix
fadeev dc2e6c5
lint
fadeev fd2e2f8
accounts import solana should correctly accept base58 private ket
fadeev 0739ade
lint
fadeev 01d5f06
fix build
fadeev db5ed3f
Changed encoding to bs58
hernan-clich 56536e9
Chaining commands
hernan-clich 96dd080
Merge branch 'main' into solana-deposit
hernan-clich b1e9f24
fix solana task import
fadeev 6638427
zod
fadeev 0e3207f
lint
fadeev c6d8cca
remove empty object inside new anchor.AnchorProvider
fadeev a4f049f
zod validation
fadeev 458783c
import solana token program
fadeev 439d823
decimals wip
fadeev a8da703
handle tx error
fadeev 255aec5
lint
fadeev 5ad56ae
check balances
fadeev 58b43da
fix build
fadeev 7a6ef82
move solana helpers
fadeev 1278c16
Merge branch 'main' into solana-deposit
fadeev 195b7a6
createSolanaCommandWithCommonOptions
fadeev ee5e98c
feat: added --name option to common solana options
fadeev ffe2311
lint
fadeev 03faf79
validate mnemonic
fadeev b35a0d5
Remove redundant refine rule
hernan-clich 65e592b
Move trim0x function to utils folder
hernan-clich 00348fb
Accept solana hex pks
hernan-clich 720d40d
Fix inconsistent error messages in test expectations.
hernan-clich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import * as anchor from "@coral-xyz/anchor"; | ||
import { Wallet } from "@coral-xyz/anchor"; | ||
import { | ||
AccountLayout, | ||
ASSOCIATED_TOKEN_PROGRAM_ID, | ||
TOKEN_PROGRAM_ID, | ||
} from "@solana/spl-token"; | ||
import { clusterApiUrl, PublicKey } from "@solana/web3.js"; | ||
import GATEWAY_DEV_IDL from "@zetachain/protocol-contracts-solana/dev/idl/gateway.json"; | ||
import GATEWAY_PROD_IDL from "@zetachain/protocol-contracts-solana/prod/idl/gateway.json"; | ||
import { ethers } from "ethers"; | ||
import { z } from "zod"; | ||
|
||
import { SolanaAccountData } from "../../../../types/accounts.types"; | ||
import { SOLANA_TOKEN_PROGRAM } from "../../../../types/shared.constants"; | ||
import { handleError, validateAndParseSchema } from "../../../../utils"; | ||
import { getAccountData } from "../../../../utils/accounts"; | ||
import { | ||
createSolanaCommandWithCommonOptions, | ||
keypairFromMnemonic, | ||
keypairFromPrivateKey, | ||
solanaDepositOptionsSchema, | ||
} from "../../../../utils/solana.commands.helpers"; | ||
|
||
type DepositOptions = z.infer<typeof solanaDepositOptionsSchema>; | ||
|
||
const main = async (options: DepositOptions) => { | ||
// Mainnet and devnet use the same IDL | ||
const gatewayIDL = | ||
options.network === "localnet" ? GATEWAY_DEV_IDL : GATEWAY_PROD_IDL; | ||
|
||
let keypair: anchor.web3.Keypair; | ||
if (options.privateKey) { | ||
keypair = keypairFromPrivateKey(options.privateKey); | ||
} else if (options.mnemonic) { | ||
keypair = await keypairFromMnemonic(options.mnemonic); | ||
} else if (options.name) { | ||
const privateKey = getAccountData<SolanaAccountData>( | ||
"solana", | ||
options.name | ||
)?.privateKey; | ||
keypair = keypairFromPrivateKey(privateKey!); | ||
} else { | ||
throw new Error("No account provided"); | ||
} | ||
|
||
let API = "http://localhost:8899"; | ||
if (options.network === "devnet") { | ||
API = clusterApiUrl("devnet"); | ||
} else if (options.network === "mainnet") { | ||
API = clusterApiUrl("mainnet-beta"); | ||
} | ||
|
||
const connection = new anchor.web3.Connection(API); | ||
|
||
const provider = new anchor.AnchorProvider(connection, new Wallet(keypair!)); | ||
|
||
const gatewayProgram = new anchor.Program(gatewayIDL as anchor.Idl, provider); | ||
|
||
const receiverBytes = ethers.getBytes(options.recipient); | ||
|
||
const tokenAccounts = await connection.getTokenAccountsByOwner( | ||
provider.wallet.publicKey, | ||
{ | ||
programId: TOKEN_PROGRAM_ID, | ||
} | ||
); | ||
|
||
try { | ||
if (options.mint) { | ||
const mintInfo = await connection.getTokenSupply( | ||
new PublicKey(options.mint) | ||
); | ||
const decimals = mintInfo.value.decimals; | ||
|
||
// Find the token account that matches the mint | ||
const matchingTokenAccount = tokenAccounts.value.find(({ account }) => { | ||
const data = AccountLayout.decode(account.data); | ||
return new PublicKey(data.mint).toBase58() === options.mint; | ||
}); | ||
|
||
if (!matchingTokenAccount) { | ||
throw new Error(`No token account found for mint ${options.mint}`); | ||
} | ||
|
||
// Check token balance | ||
const accountInfo = await connection.getTokenAccountBalance( | ||
matchingTokenAccount.pubkey | ||
); | ||
const balance = accountInfo.value.uiAmount; | ||
const amountToSend = parseFloat(options.amount); | ||
if (!balance || balance < amountToSend) { | ||
throw new Error( | ||
`Insufficient token balance. Available: ${ | ||
balance ?? 0 | ||
}, Required: ${amountToSend}` | ||
); | ||
} | ||
|
||
const from = matchingTokenAccount.pubkey; | ||
|
||
// Find the TSS PDA (meta) | ||
const [tssPda] = PublicKey.findProgramAddressSync( | ||
[Buffer.from("meta", "utf-8")], | ||
gatewayProgram.programId | ||
); | ||
|
||
// Find the TSS's ATA for the mint | ||
const tssAta = await PublicKey.findProgramAddress( | ||
[ | ||
tssPda.toBuffer(), | ||
TOKEN_PROGRAM_ID.toBuffer(), | ||
new PublicKey(options.mint).toBuffer(), | ||
], | ||
ASSOCIATED_TOKEN_PROGRAM_ID | ||
); | ||
|
||
const to = tssAta[0].toBase58(); | ||
|
||
const tx = await gatewayProgram.methods | ||
.depositSplToken( | ||
new anchor.BN(ethers.parseUnits(options.amount, decimals).toString()), | ||
receiverBytes, | ||
null | ||
) | ||
.accounts({ | ||
from, | ||
mintAccount: options.mint, | ||
signer: keypair!.publicKey, | ||
systemProgram: anchor.web3.SystemProgram.programId, | ||
to, | ||
tokenProgram: options.tokenProgram, | ||
}) | ||
.rpc(); | ||
console.log("Transaction hash:", tx); | ||
} else { | ||
// Check SOL balance | ||
const balance = await connection.getBalance(keypair!.publicKey); | ||
const lamportsNeeded = ethers.parseUnits(options.amount, 9).toString(); | ||
if (balance < parseInt(lamportsNeeded)) { | ||
throw new Error( | ||
`Insufficient SOL balance. Available: ${balance / 1e9}, Required: ${ | ||
options.amount | ||
}` | ||
); | ||
} | ||
const tx = await gatewayProgram.methods | ||
.deposit( | ||
new anchor.BN(ethers.parseUnits(options.amount, 9).toString()), | ||
receiverBytes, | ||
null | ||
) | ||
.accounts({}) | ||
.rpc(); | ||
console.log("Transaction hash:", tx); | ||
} | ||
} catch (error) { | ||
handleError({ | ||
context: "Error during deposit", | ||
error, | ||
shouldThrow: false, | ||
}); | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
export const depositCommand = createSolanaCommandWithCommonOptions("deposit") | ||
.description("Deposit tokens from Solana") | ||
.requiredOption("--amount <amount>", "Amount of tokens to deposit") | ||
.option( | ||
"--token-program <tokenProgram>", | ||
"Token program", | ||
SOLANA_TOKEN_PROGRAM | ||
) | ||
.option("--mint <mint>", "SPL token mint address") | ||
.action(async (options) => { | ||
const validatedOptions = validateAndParseSchema( | ||
options, | ||
solanaDepositOptionsSchema, | ||
{ | ||
exitOnError: true, | ||
} | ||
); | ||
await main(validatedOptions); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
import { Command } from "commander"; | ||
|
||
import { depositCommand } from "./deposit"; | ||
import { encodeCommand } from "./encode"; | ||
|
||
export const solanaCommand = new Command("solana").description( | ||
"Solana commands" | ||
); | ||
|
||
solanaCommand.addCommand(encodeCommand).helpCommand(false); | ||
export const solanaCommand = new Command("solana") | ||
.description("Solana commands") | ||
.addCommand(depositCommand) | ||
.addCommand(encodeCommand) | ||
.helpCommand(false); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.