|
| 1 | +const fs = require('fs'); |
| 2 | +const web3 = require("@solana/web3.js"); |
| 3 | +const {struct, b, u8, blob} = require("@solana/buffer-layout"); |
| 4 | + |
| 5 | +export const invoke = async (loan: string, collateral: string) => { |
| 6 | + /* Obtain the contract keypair */ |
| 7 | + var contract; |
| 8 | + try { |
| 9 | + let data = fs.readFileSync( |
| 10 | + '../build/example_sol_contract-keypair.json' |
| 11 | + ); |
| 12 | + contract = web3.Keypair.fromSecretKey( |
| 13 | + new Uint8Array(JSON.parse(data)) |
| 14 | + ); |
| 15 | + console.info("Invoking contract " + contract.publicKey); |
| 16 | + } catch (error) { |
| 17 | + console.error("Please run scripts/build.sh first."); |
| 18 | + return; |
| 19 | + } |
| 20 | + |
| 21 | + /* Prepare the payer account */ |
| 22 | + let conn = new web3.Connection(web3.clusterApiUrl('devnet')); |
| 23 | + console.info("Airdropping to the payer account..."); |
| 24 | + let payer = web3.Keypair.generate(); |
| 25 | + let airdropSig = await conn.requestAirdrop( |
| 26 | + payer.publicKey, web3.LAMPORTS_PER_SOL |
| 27 | + ); |
| 28 | + await conn.confirmTransaction(airdropSig); |
| 29 | + |
| 30 | + /* Prepare the createInst instruction which creates an |
| 31 | + * account storing the AdminConfig data for the instructions */ |
| 32 | + let loanInfoSize = 1 + 32 + 32; |
| 33 | + let dataAccount = web3.Keypair.generate(); |
| 34 | + let dataCost = await conn.getMinimumBalanceForRentExemption(loanInfoSize); |
| 35 | + const createInst = web3.SystemProgram.createAccount({ |
| 36 | + lamports: dataCost, |
| 37 | + space: loanInfoSize, |
| 38 | + programId: contract.publicKey, |
| 39 | + fromPubkey: payer.publicKey, |
| 40 | + newAccountPubkey: dataAccount.publicKey, |
| 41 | + }); |
| 42 | + |
| 43 | + /* Prepare the accounts and instruction data for transactions */ |
| 44 | + const dataKey = dataAccount.publicKey; |
| 45 | + const loanKey = new web3.PublicKey(loan); |
| 46 | + const collateralKey = new web3.PublicKey(collateral); |
| 47 | + let accounts = |
| 48 | + [{pubkey: contract.publicKey, isSigner: true, isWritable: false}, |
| 49 | + {pubkey: dataKey, isSigner: false, isWritable: false}, |
| 50 | + {pubkey: loanKey, isSigner: false, isWritable: false}, |
| 51 | + {pubkey: collateralKey, isSigner: false, isWritable: false}, |
| 52 | + ]; |
| 53 | + |
| 54 | + let initLayout = struct([ u8('instruction') ]) |
| 55 | + let initData = Buffer.alloc(initLayout.span); |
| 56 | + let loan2ValueLayout = struct([ |
| 57 | + u8('instruction'), blob(8, 'loan_qty'), blob(8, 'collateral_qty') |
| 58 | + ]) |
| 59 | + let loan2ValueData = Buffer.alloc(loan2ValueLayout.span); |
| 60 | + |
| 61 | + /* Invoke the Init instruction (instruction #0) */ |
| 62 | + console.log("Creating data account and invoking Init..."); |
| 63 | + initLayout.encode({instruction: 0}, initData); |
| 64 | + let txInit = new web3.Transaction({ feePayer: payer.publicKey }); |
| 65 | + txInit.add( |
| 66 | + createInst, /* Create data account */ |
| 67 | + new web3.TransactionInstruction({ /* Initialize data account */ |
| 68 | + data: initData, |
| 69 | + keys: accounts, |
| 70 | + programId: contract.publicKey |
| 71 | + }) |
| 72 | + ); |
| 73 | + let txInitSig = await web3.sendAndConfirmTransaction( |
| 74 | + conn, txInit, [payer, dataAccount, contract] |
| 75 | + ); |
| 76 | + console.log("TxHash: " + txInitSig); |
| 77 | + |
| 78 | + /* Invoke the Loan2Value instruction (instruction #1) */ |
| 79 | + console.log("Checking loan to value ratio..."); |
| 80 | + /* Encode 0x1 in big ending */ |
| 81 | + let loan_qty = Buffer.from('0100000000000000', 'hex'); |
| 82 | + /* Encode 0xbb8 (3000) in big ending */ |
| 83 | + let collateral_qty = Buffer.from('b80b000000000000', 'hex'); |
| 84 | + loan2ValueLayout.encode( |
| 85 | + {instruction: 1, |
| 86 | + loan_qty: blob(8).decode(loan_qty), |
| 87 | + collateral_qty: blob(8).decode(collateral_qty)} |
| 88 | + , loan2ValueData); |
| 89 | + |
| 90 | + let txCheck = new web3.Transaction({ feePayer: payer.publicKey }); |
| 91 | + txCheck.add( |
| 92 | + new web3.TransactionInstruction({ |
| 93 | + data: loan2ValueData, |
| 94 | + keys: accounts, |
| 95 | + programId: contract.publicKey |
| 96 | + }) |
| 97 | + ); |
| 98 | + let txCheckSig = await web3.sendAndConfirmTransaction( |
| 99 | + conn, txCheck, [payer, contract] |
| 100 | + ); |
| 101 | + console.log("TxHash: " + txCheckSig); |
| 102 | + |
| 103 | + /* Try to invoke the Init instruction without authority */ |
| 104 | + console.log("Trying an unauthorized invocation of Init..."); |
| 105 | + let attacker = web3.Keypair.generate(); |
| 106 | + accounts[0].pubkey = attacker.publicKey |
| 107 | + |
| 108 | + let attackerDataAccount = web3.Keypair.generate(); |
| 109 | + const attackerCreateInst = web3.SystemProgram.createAccount({ |
| 110 | + lamports: dataCost, |
| 111 | + space: loanInfoSize, |
| 112 | + programId: contract.publicKey, |
| 113 | + fromPubkey: payer.publicKey, |
| 114 | + newAccountPubkey: attackerDataAccount.publicKey, |
| 115 | + }); |
| 116 | + |
| 117 | + let txAttacker = new web3.Transaction({ feePayer: payer.publicKey }); |
| 118 | + txAttacker.add( |
| 119 | + attackerCreateInst, |
| 120 | + new web3.TransactionInstruction({ |
| 121 | + data: initData, |
| 122 | + keys: accounts, |
| 123 | + programId: contract.publicKey |
| 124 | + }) |
| 125 | + ); |
| 126 | + |
| 127 | + var attacker_succeed = false, txAttackerSig; |
| 128 | + try { |
| 129 | + txAttackerSig = await web3.sendAndConfirmTransaction( |
| 130 | + conn, txAttacker, [payer, attackerDataAccount, attacker] |
| 131 | + ); |
| 132 | + attacker_succeed = true; |
| 133 | + } catch (error) { |
| 134 | + console.log("Attacker failed to invoke unauthorized Init."); |
| 135 | + } |
| 136 | + |
| 137 | + if (attacker_succeed) |
| 138 | + throw new Error("Attacker succeeded! TxHash: " + txAttackerSig); |
| 139 | +} |
| 140 | + |
| 141 | +/* Pyth price accounts on the solana devnet */ |
| 142 | +let ethToUSD = "EdVCmQ9FSPcVe5YySXDPCRmc8aDQLKJ9xvYBMZPie1Vw"; |
| 143 | +let usdtToUSD = "38xoQ4oeJCBrcVvca2cGk7iV1dAfrmTR1kmhSCJQ8Jto"; |
| 144 | +invoke(ethToUSD, usdtToUSD); |
0 commit comments