Skip to content
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

fix: fix solana inbounds #3261

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/e2etests/test_solana_deposit_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ func TestSolanaDepositAndCall(r *runner.E2ERunner, args []string) {
utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined)

// check if example contract has been called, bar value should be set to amount
utils.MustHaveCalledExampleContract(r, contract, depositAmount)
utils.MustHaveCalledExampleContractWithMsg(r, contract, depositAmount, data)
}
2 changes: 1 addition & 1 deletion e2e/e2etests/test_spl_deposit_and_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestSPLDepositAndCall(r *runner.E2ERunner, args []string) {
utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined)

// check if example contract has been called, bar value should be set to amount
utils.MustHaveCalledExampleContract(r, contract, big.NewInt(int64(amount)))
utils.MustHaveCalledExampleContractWithMsg(r, contract, big.NewInt(int64(amount)), data)

// verify balances are updated
pdaBalanceAfter, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, pdaAta, rpc.CommitmentFinalized)
Expand Down
48 changes: 36 additions & 12 deletions e2e/runner/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,24 @@ func (r *E2ERunner) CreateDepositInstruction(
data []byte,
amount uint64,
) solana.Instruction {
depositData, err := borsh.Serialize(solanacontract.DepositInstructionParams{
Discriminator: solanacontract.DiscriminatorDeposit,
Amount: amount,
Memo: append(receiver.Bytes(), data...),
})
require.NoError(r, err)
var err error
var depositData []byte
if data == nil {
depositData, err = borsh.Serialize(solanacontract.DepositInstructionParams{
Discriminator: solanacontract.DiscriminatorDeposit,
Amount: amount,
Receiver: receiver,
})
require.NoError(r, err)
} else {
depositData, err = borsh.Serialize(solanacontract.DepositAndCallInstructionParams{
Discriminator: solanacontract.DiscriminatorDepositAndCall,
Amount: amount,
Receiver: receiver,
Memo: data,
})
require.NoError(r, err)
}

return &solana.GenericInstruction{
ProgID: r.GatewayProgram,
Expand Down Expand Up @@ -87,12 +99,24 @@ func (r *E2ERunner) CreateDepositSPLInstruction(
receiver ethcommon.Address,
data []byte,
) solana.Instruction {
depositSPLData, err := borsh.Serialize(solanacontract.DepositInstructionParams{
Discriminator: solanacontract.DiscriminatorDepositSPL,
Amount: amount,
Memo: append(receiver.Bytes(), data...),
})
require.NoError(r, err)
var err error
var depositSPLData []byte
if data == nil {
depositSPLData, err = borsh.Serialize(solanacontract.DepositSPLInstructionParams{
Discriminator: solanacontract.DiscriminatorDepositSPL,
Amount: amount,
Receiver: receiver,
})
require.NoError(r, err)
} else {
depositSPLData, err = borsh.Serialize(solanacontract.DepositSPLAndCallInstructionParams{
Discriminator: solanacontract.DiscriminatorDepositSPLAndCall,
Amount: amount,
Receiver: receiver,
Memo: data,
})
require.NoError(r, err)
}

return &solana.GenericInstruction{
ProgID: r.GatewayProgram,
Expand Down
23 changes: 23 additions & 0 deletions e2e/utils/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,26 @@ func MustHaveCalledExampleContract(
amount.String(),
)
}

// MustHaveCalledExampleContractWithMsg checks if the contract has been called correctly with correct amount and msg
func MustHaveCalledExampleContractWithMsg(
t require.TestingT,
contract *testcontract.Example,
amount *big.Int,
msg []byte,
) {
bar, err := contract.Bar(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(
t,
0,
bar.Cmp(amount),
"cross-chain call failed bar value %s should be equal to amount %s",
bar.String(),
amount.String(),
)

lastMsg, err := contract.LastMessage(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, string(msg), string(lastMsg))
}
6 changes: 6 additions & 0 deletions pkg/contracts/solana/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ var (
// DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit' instruction
DiscriminatorDeposit = idlgateway.IDLGateway.GetDiscriminator("deposit")

// DiscriminatorDeposit returns the discriminator for Solana gateway 'deposit_and_call' instruction
DiscriminatorDepositAndCall = idlgateway.IDLGateway.GetDiscriminator("deposit_and_call")

// DiscriminatorDepositSPL returns the discriminator for Solana gateway 'deposit_spl_token' instruction
DiscriminatorDepositSPL = idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token")

// DiscriminatorDepositSPLAndCall returns the discriminator for Solana gateway 'deposit_spl_token_and_call' instruction
DiscriminatorDepositSPLAndCall = idlgateway.IDLGateway.GetDiscriminator("deposit_spl_token_and_call")

// DiscriminatorWithdraw returns the discriminator for Solana gateway 'withdraw' instruction
DiscriminatorWithdraw = idlgateway.IDLGateway.GetDiscriminator("withdraw")

Expand Down
132 changes: 116 additions & 16 deletions pkg/contracts/solana/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,33 @@
Asset string
}

// ParseInboundAsDeposit tries to parse an instruction as a 'deposit'.
// It returns nil if the instruction can't be parsed as a 'deposit'.
// ParseInboundAsDeposit tries to parse an instruction as a 'deposit' or 'deposit_and_call'.
// It returns nil if the instruction can't be parsed.
func ParseInboundAsDeposit(
tx *solana.Transaction,
instructionIndex int,
slot uint64,
) (*Deposit, error) {
// first try to parse as deposit, then as deposit_and_call
deposit, err := tryParseAsDeposit(tx, instructionIndex, slot)
if err != nil || deposit != nil {
return deposit, err
}

return tryParseAsDepositAndCall(tx, instructionIndex, slot)
}

// tryParseAsDeposit tries to parse instruction as deposit
func tryParseAsDeposit(
tx *solana.Transaction,
instructionIndex int,
slot uint64,
) (*Deposit, error) {
// get instruction by index
instruction := tx.Message.Instructions[instructionIndex]

// try deserializing instruction as a 'deposit'
var inst DepositInstructionParams
// try deserializing instruction as a deposit
inst := DepositInstructionParams{}
err := borsh.Deserialize(&inst, instruction.Data)
if err != nil {
return nil, nil
Expand All @@ -51,30 +66,80 @@
return &Deposit{
Sender: sender,
Amount: inst.Amount,
Memo: inst.Memo,
Memo: inst.Receiver[:],
Slot: slot,
Asset: "", // no asset for gas token SOL
}, nil
}

// tryParseAsDepositAndCall tries to parse instruction as deposit_and_call
func tryParseAsDepositAndCall(
tx *solana.Transaction,
instructionIndex int,
slot uint64,
) (*Deposit, error) {
// get instruction by index
instruction := tx.Message.Instructions[instructionIndex]

// try deserializing instruction as a deposit_and_call
instDepositAndCall := DepositAndCallInstructionParams{}
err := borsh.Deserialize(&instDepositAndCall, instruction.Data)
if err != nil {
return nil, nil
}

// check if the instruction is a deposit_and_call or not, if not, skip parsing
if instDepositAndCall.Discriminator != DiscriminatorDepositAndCall {
return nil, nil
}

Check warning on line 94 in pkg/contracts/solana/inbound.go

View check run for this annotation

Codecov / codecov/patch

pkg/contracts/solana/inbound.go#L93-L94

Added lines #L93 - L94 were not covered by tests
lumtis marked this conversation as resolved.
Show resolved Hide resolved

// get the sender address (skip if unable to parse signer address)
sender, err := getSignerDeposit(tx, &instruction)
if err != nil {
return nil, err
}
return &Deposit{
Sender: sender,
Amount: instDepositAndCall.Amount,
Memo: append(instDepositAndCall.Receiver[:], instDepositAndCall.Memo...),
Slot: slot,
Asset: "", // no asset for gas token SOL
}, nil
}

// ParseInboundAsDepositSPL tries to parse an instruction as a 'deposit_spl_token'.
// It returns nil if the instruction can't be parsed as a 'deposit_spl_token'.
// ParseInboundAsDepositSPL tries to parse an instruction as a deposit_spl or deposit_spl_and_call.
// It returns nil if the instruction can't be parsed as a deposit_spl.
func ParseInboundAsDepositSPL(
tx *solana.Transaction,
instructionIndex int,
slot uint64,
) (*Deposit, error) {
// first try to parse as deposit_spl, then as deposit_spl_and_call
deposit, err := tryParseAsDepositSPL(tx, instructionIndex, slot)
if err != nil || deposit != nil {
return deposit, err
}

return tryParseAsDepositSPLAndCall(tx, instructionIndex, slot)
}

// tryParseAsDepositSPL tries to parse instruction as deposit_spl
func tryParseAsDepositSPL(
tx *solana.Transaction,
instructionIndex int,
slot uint64,
) (*Deposit, error) {
// get instruction by index
instruction := tx.Message.Instructions[instructionIndex]

// try deserializing instruction as a 'deposit_spl_token'
// try deserializing instruction as a deposit_spl
var inst DepositSPLInstructionParams
err := borsh.Deserialize(&inst, instruction.Data)
if err != nil {
return nil, nil
}

// check if the instruction is a deposit spl or not, if not, skip parsing
// check if the instruction is a deposit_spl or not, if not, skip parsing
if inst.Discriminator != DiscriminatorDepositSPL {
return nil, nil
}
Expand All @@ -88,7 +153,42 @@
return &Deposit{
Sender: sender,
Amount: inst.Amount,
Memo: inst.Memo,
Memo: inst.Receiver[:],
Slot: slot,
Asset: spl,
}, nil
}

// tryParseAsDepositSPLAndCall tries to parse instruction as deposit_spl_and_call
func tryParseAsDepositSPLAndCall(
tx *solana.Transaction,
instructionIndex int,
slot uint64,
) (*Deposit, error) {
// get instruction by index
instruction := tx.Message.Instructions[instructionIndex]

// try deserializing instruction as a deposit_spl_and_call
instDepositAndCall := DepositSPLAndCallInstructionParams{}
err := borsh.Deserialize(&instDepositAndCall, instruction.Data)
if err != nil {
return nil, nil
}

// check if the instruction is a deposit_spl_and_call or not, if not, skip parsing
if instDepositAndCall.Discriminator != DiscriminatorDepositSPLAndCall {
return nil, nil
}

Check warning on line 181 in pkg/contracts/solana/inbound.go

View check run for this annotation

Codecov / codecov/patch

pkg/contracts/solana/inbound.go#L180-L181

Added lines #L180 - L181 were not covered by tests

// get the sender and spl addresses
sender, spl, err := getSignerAndSPLFromDepositSPLAccounts(tx, &instruction)
if err != nil {
return nil, err
}
return &Deposit{
Sender: sender,
Amount: instDepositAndCall.Amount,
Memo: append(instDepositAndCall.Receiver[:], instDepositAndCall.Memo...),
Slot: slot,
Asset: spl,
}, nil
Expand All @@ -101,9 +201,9 @@
return "", err
}

// there should be 3 accounts for a deposit instruction
if len(instructionAccounts) != accountsNumDeposit {
return "", fmt.Errorf("want %d accounts, got %d", accountsNumDeposit, len(instructionAccounts))
// there should be at least all mandatory accounts for a deposit instruction
if len(instructionAccounts) < accountsNumDeposit {
return "", fmt.Errorf("want required %d accounts, got %d", accountsNumDeposit, len(instructionAccounts))
}

// the accounts are [signer, pda, system_program]
Expand All @@ -125,10 +225,10 @@
return "", "", err
}

// there should be 7 accounts for a deposit spl instruction
if len(instructionAccounts) != accountsNumberDepositSPL {
// there should be at least all mandatory accounts for a deposit spl instruction
if len(instructionAccounts) < accountsNumberDepositSPL {
return "", "", fmt.Errorf(
"want %d accounts, got %d",
"want required %d accounts, got %d",
accountsNumberDepositSPL,
len(instructionAccounts),
)
Expand Down
Loading
Loading