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

Implement formatCallDAta for Cairo2 #399

Merged
merged 5 commits into from
Oct 10, 2023
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
78 changes: 54 additions & 24 deletions account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ var (
//go:generate mockgen -destination=../mocks/mock_account.go -package=mocks -source=account.go AccountInterface
type AccountInterface interface {
Sign(ctx context.Context, msg *felt.Felt) ([]*felt.Felt, error)
BuildInvokeTx(ctx context.Context, invokeTx *rpc.InvokeTxnV1, fnCall *[]rpc.FunctionCall) error
TransactionHashInvoke(invokeTxn rpc.InvokeTxnType) (*felt.Felt, error)
TransactionHashDeployAccount(tx rpc.DeployAccountTxn, contractAddress *felt.Felt) (*felt.Felt, error)
TransactionHashDeclare(tx rpc.DeclareTxnType) (*felt.Felt, error)
Expand Down Expand Up @@ -317,12 +316,6 @@ func (account *Account) WaitForTransactionReceipt(ctx context.Context, transacti
}
}

// BuildInvokeTx formats the calldata and signs the transaction
func (account *Account) BuildInvokeTx(ctx context.Context, invokeTx *rpc.InvokeTxnV1, fnCall *[]rpc.FunctionCall) error {
invokeTx.Calldata = FmtCalldata(*fnCall)
return account.SignInvokeTransaction(ctx, invokeTx)
}

func (account *Account) AddInvokeTransaction(ctx context.Context, invokeTx rpc.InvokeTxnV1) (*rpc.AddInvokeTransactionResponse, error) {
return account.provider.AddInvokeTransaction(ctx, invokeTx)
}
Expand Down Expand Up @@ -420,26 +413,63 @@ func (account *Account) TransactionByHash(ctx context.Context, hash *felt.Felt)
return account.provider.TransactionByHash(ctx, hash)
}

func (account *Account) FmtCalldata(fnCalls []rpc.FunctionCall, cairoVersion int) ([]*felt.Felt, error) {
switch cairoVersion {
case 0:
return FmtCalldataCairo0(fnCalls), nil
case 2:
cicr99 marked this conversation as resolved.
Show resolved Hide resolved
return FmtCalldataCairo2(fnCalls), nil
default:
return nil, errors.New("Cairo version not supported")
}
}

/*
Formats the multicall transactions in a format which can be signed and verified by the network and OpenZeppelin account contracts
Formats the call data for Cairo0 contracts
*/
func FmtCalldata(fnCalls []rpc.FunctionCall) []*felt.Felt {
callArray := []*felt.Felt{}
callData := []*felt.Felt{new(felt.Felt).SetUint64(uint64(len(fnCalls)))}

for _, tx := range fnCalls {
callData = append(callData, tx.ContractAddress, tx.EntryPointSelector)
func FmtCalldataCairo0(fnCalls []rpc.FunctionCall) []*felt.Felt {
execCallData := []*felt.Felt{}
execCallData = append(execCallData, new(felt.Felt).SetUint64(uint64(len(fnCalls))))

// Legacy : Cairo 0
concatCallData := []*felt.Felt{}
for _, fnCall := range fnCalls {
execCallData = append(
execCallData,
fnCall.ContractAddress,
fnCall.EntryPointSelector,
new(felt.Felt).SetUint64(uint64(len(concatCallData))),
new(felt.Felt).SetUint64(uint64(len(fnCall.Calldata))+1),
cicr99 marked this conversation as resolved.
Show resolved Hide resolved
)
concatCallData = append(concatCallData, fnCall.Calldata...)
}
execCallData = append(execCallData, new(felt.Felt).SetUint64(uint64(len(concatCallData))+1))
execCallData = append(execCallData, concatCallData...)
execCallData = append(execCallData, new(felt.Felt).SetUint64(0))

if len(tx.Calldata) == 0 {
callData = append(callData, &felt.Zero, &felt.Zero)
continue
}
return execCallData
}

callData = append(callData, new(felt.Felt).SetUint64(uint64(len(callArray))), new(felt.Felt).SetUint64(uint64(len(tx.Calldata))+1))
callArray = append(callArray, tx.Calldata...)
/*
Formats the call data for Cairo 2 contracs
*/
func FmtCalldataCairo2(fnCalls []rpc.FunctionCall) []*felt.Felt {
execCallData := []*felt.Felt{}
execCallData = append(execCallData, new(felt.Felt).SetUint64(uint64(len(fnCalls))))

concatCallData := []*felt.Felt{}
for _, fnCall := range fnCalls {
execCallData = append(
execCallData,
fnCall.ContractAddress,
fnCall.EntryPointSelector,
new(felt.Felt).SetUint64(uint64(len(concatCallData))),
new(felt.Felt).SetUint64(uint64(len(fnCall.Calldata))),
)
concatCallData = append(concatCallData, fnCall.Calldata...)
}
callData = append(callData, new(felt.Felt).SetUint64(uint64(len(callArray)+1)))
callData = append(callData, callArray...)
callData = append(callData, new(felt.Felt).SetUint64(0))
return callData
execCallData = append(execCallData, new(felt.Felt).SetUint64(uint64(len(concatCallData))))
execCallData = append(execCallData, concatCallData...)

return execCallData
}
170 changes: 126 additions & 44 deletions account/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,22 @@ func TestTransactionHashInvoke(t *testing.T) {
}

func TestFmtCallData(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockRpcProvider := mocks.NewMockRpcProvider(mockCtrl)

type testSetType struct {
CairoVersion int
ChainID string
FnCall rpc.FunctionCall
ExpectedCallData []*felt.Felt
}
testSet := map[string][]testSetType{
"devnet": {},
"mock": {
{
CairoVersion: 0,
ChainID: "SN_GOERLI",
FnCall: rpc.FunctionCall{
ContractAddress: utils.TestHexToFelt(t, "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
EntryPointSelector: utils.GetSelectorFromNameFelt("transfer"),
Expand All @@ -191,7 +199,12 @@ func TestFmtCallData(t *testing.T) {
}[testEnv]

for _, test := range testSet {
fmtCallData := account.FmtCalldata([]rpc.FunctionCall{test.FnCall})
mockRpcProvider.EXPECT().ChainID(context.Background()).Return(test.ChainID, nil)
acnt, err := account.NewAccount(mockRpcProvider, &felt.Zero, "pubkey", account.NewMemKeystore())
require.NoError(t, err)

fmtCallData, err := acnt.FmtCalldata([]rpc.FunctionCall{test.FnCall}, test.CairoVersion)
require.NoError(t, err)
require.Equal(t, fmtCallData, test.ExpectedCallData)
}
}
Expand Down Expand Up @@ -315,51 +328,52 @@ func TestSignMOCK(t *testing.T) {
func TestAddInvoke(t *testing.T) {

type testSetType struct {
ExpectedHash *felt.Felt
ExpectedError *rpc.RPCError
SetKS bool
AccountAddress *felt.Felt
PubKey *felt.Felt
PrivKey *felt.Felt
InvokeTx rpc.InvokeTxnV1
FnCall rpc.FunctionCall
TxDetails rpc.TxDetails
ExpectedError *rpc.RPCError
CairoContractVersion int
SetKS bool
AccountAddress *felt.Felt
PubKey *felt.Felt
PrivKey *felt.Felt
InvokeTx rpc.InvokeTxnV1
FnCall rpc.FunctionCall
TxDetails rpc.TxDetails
}
testSet := map[string][]testSetType{
"mock": {},
"devnet": {},
"testnet": {{
// https://goerli.voyager.online/tx/0x73cf79c4bfa0c7a41f473c07e1be5ac25faa7c2fdf9edcbd12c1438f40f13d8#overview
ExpectedHash: utils.TestHexToFelt(t, "0x73cf79c4bfa0c7a41f473c07e1be5ac25faa7c2fdf9edcbd12c1438f40f13d8"),
ExpectedError: rpc.ErrDuplicateTx,
AccountAddress: utils.TestHexToFelt(t, "0x043784df59268c02b716e20bf77797bd96c68c2f100b2a634e448c35e3ad363e"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x049f060d2dffd3bf6f2c103b710baf519530df44529045f92c3903097e8d861f"),
PrivKey: utils.TestHexToFelt(t, "0x043b7fe9d91942c98cd5fd37579bd99ec74f879c4c79d886633eecae9dad35fa"),
InvokeTx: rpc.InvokeTxnV1{
Nonce: new(felt.Felt).SetUint64(2),
MaxFee: utils.TestHexToFelt(t, "0x574fbde6000"),
Version: rpc.TransactionV1,
Type: rpc.TransactionType_Invoke,
SenderAddress: utils.TestHexToFelt(t, "0x043784df59268c02b716e20bf77797bd96c68c2f100b2a634e448c35e3ad363e"),
},
FnCall: rpc.FunctionCall{
ContractAddress: utils.TestHexToFelt(t, "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
EntryPointSelector: utils.GetSelectorFromNameFelt("transfer"),
Calldata: []*felt.Felt{
utils.TestHexToFelt(t, "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
utils.TestHexToFelt(t, "0x1"),
"testnet": {
{
// https://goerli.voyager.online/tx/0x73cf79c4bfa0c7a41f473c07e1be5ac25faa7c2fdf9edcbd12c1438f40f13d8#overview
ExpectedError: rpc.ErrDuplicateTx,
CairoContractVersion: 0,
AccountAddress: utils.TestHexToFelt(t, "0x043784df59268c02b716e20bf77797bd96c68c2f100b2a634e448c35e3ad363e"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x049f060d2dffd3bf6f2c103b710baf519530df44529045f92c3903097e8d861f"),
PrivKey: utils.TestHexToFelt(t, "0x043b7fe9d91942c98cd5fd37579bd99ec74f879c4c79d886633eecae9dad35fa"),
InvokeTx: rpc.InvokeTxnV1{
Nonce: new(felt.Felt).SetUint64(2),
MaxFee: utils.TestHexToFelt(t, "0x574fbde6000"),
Version: rpc.TransactionV1,
Type: rpc.TransactionType_Invoke,
SenderAddress: utils.TestHexToFelt(t, "0x043784df59268c02b716e20bf77797bd96c68c2f100b2a634e448c35e3ad363e"),
},
FnCall: rpc.FunctionCall{
ContractAddress: utils.TestHexToFelt(t, "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
EntryPointSelector: utils.GetSelectorFromNameFelt("transfer"),
Calldata: []*felt.Felt{
utils.TestHexToFelt(t, "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
utils.TestHexToFelt(t, "0x1"),
},
},
},
},
{
// https://goerli.voyager.online/tx/0x171537c58b16db45aeec3d3f493617cd3dd571561b856c115dc425b85212c86#overview
ExpectedHash: utils.TestHexToFelt(t, "0x171537c58b16db45aeec3d3f493617cd3dd571561b856c115dc425b85212c86"),
ExpectedError: rpc.ErrDuplicateTx,
AccountAddress: utils.TestHexToFelt(t, "0x043784df59268c02b716e20bf77797bd96c68c2f100b2a634e448c35e3ad363e"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x049f060d2dffd3bf6f2c103b710baf519530df44529045f92c3903097e8d861f"),
PrivKey: utils.TestHexToFelt(t, "0x043b7fe9d91942c98cd5fd37579bd99ec74f879c4c79d886633eecae9dad35fa"),
ExpectedError: rpc.ErrDuplicateTx,
CairoContractVersion: 0,
AccountAddress: utils.TestHexToFelt(t, "0x043784df59268c02b716e20bf77797bd96c68c2f100b2a634e448c35e3ad363e"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x049f060d2dffd3bf6f2c103b710baf519530df44529045f92c3903097e8d861f"),
PrivKey: utils.TestHexToFelt(t, "0x043b7fe9d91942c98cd5fd37579bd99ec74f879c4c79d886633eecae9dad35fa"),
InvokeTx: rpc.InvokeTxnV1{
Nonce: new(felt.Felt).SetUint64(6),
MaxFee: utils.TestHexToFelt(t, "0x9184e72a000"),
Expand All @@ -376,12 +390,77 @@ func TestAddInvoke(t *testing.T) {
},
},
},
{
// https://goerli.voyager.online/tx/0x1bc0f8c04584735ea9e4485f927c25a6e025bda3117beb508cd1bb5e41f08d9
ExpectedError: rpc.ErrDuplicateTx,
CairoContractVersion: 2,
AccountAddress: utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
PrivKey: utils.TestHexToFelt(t, "0x07514c4f0de1f800b0b0c7377ef39294ce218a7abd9a1c9b6aa574779f7cdc6a"),
InvokeTx: rpc.InvokeTxnV1{
Nonce: new(felt.Felt).SetUint64(6),
MaxFee: utils.TestHexToFelt(t, "0x9184e72a000"),
Version: rpc.TransactionV1,
Type: rpc.TransactionType_Invoke,
SenderAddress: utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
},
FnCall: rpc.FunctionCall{
ContractAddress: utils.TestHexToFelt(t, "0x05044dfb70b9475663e3ddddb11bbbeccc71614b8db86fc3dc0c16b2b9d3151d"),
EntryPointSelector: utils.GetSelectorFromNameFelt("increase_value_8"),
Calldata: []*felt.Felt{
utils.TestHexToFelt(t, "0x1234"),
},
},
},
{
// https://goerli.voyager.online/tx/0xe8cdb03ddc6b65c2c268eb8084bef41ef63009c10a38f8d1e167652a721588
ExpectedError: rpc.ErrDuplicateTx,
CairoContractVersion: 2,
AccountAddress: utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
PrivKey: utils.TestHexToFelt(t, "0x07514c4f0de1f800b0b0c7377ef39294ce218a7abd9a1c9b6aa574779f7cdc6a"),
InvokeTx: rpc.InvokeTxnV1{
Nonce: new(felt.Felt).SetUint64(7),
MaxFee: utils.TestHexToFelt(t, "0x9184e72a000"),
Version: rpc.TransactionV1,
Type: rpc.TransactionType_Invoke,
SenderAddress: utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
},
FnCall: rpc.FunctionCall{
ContractAddress: utils.TestHexToFelt(t, "0x05044dfb70b9475663e3ddddb11bbbeccc71614b8db86fc3dc0c16b2b9d3151d"),
EntryPointSelector: utils.GetSelectorFromNameFelt("increase_value"),
Calldata: []*felt.Felt{},
},
},
{
// https://goerli.voyager.online/tx/0xdcec9fdd48440243fa8fdb8bf87cc40d5ef91181d5a4a0304140df5701c238
ExpectedError: rpc.ErrDuplicateTx,
CairoContractVersion: 2,
AccountAddress: utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
SetKS: true,
PubKey: utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
PrivKey: utils.TestHexToFelt(t, "0x07514c4f0de1f800b0b0c7377ef39294ce218a7abd9a1c9b6aa574779f7cdc6a"),
InvokeTx: rpc.InvokeTxnV1{
Nonce: new(felt.Felt).SetUint64(18),
MaxFee: utils.TestHexToFelt(t, "0x9184e72a000"),
Version: rpc.TransactionV1,
Type: rpc.TransactionType_Invoke,
SenderAddress: utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
},
FnCall: rpc.FunctionCall{
ContractAddress: utils.TestHexToFelt(t, "0x05044dfb70b9475663e3ddddb11bbbeccc71614b8db86fc3dc0c16b2b9d3151d"),
EntryPointSelector: utils.GetSelectorFromNameFelt("increase_value_8"),
Calldata: []*felt.Felt{utils.TestHexToFelt(t, "0xaC25b2B9F4ca06179fA0D2522F47Bc86A9DF9314")},
},
},
},
"mainnet": {},
}[testEnv]

for _, test := range testSet {
client, err := rpc.NewClient(base + "/rpc")
client, err := rpc.NewClient(base)
require.NoError(t, err, "Error in rpc.NewClient")
provider := rpc.NewProvider(client)

Expand All @@ -396,15 +475,18 @@ func TestAddInvoke(t *testing.T) {
acnt, err := account.NewAccount(provider, test.AccountAddress, test.PubKey.String(), ks)
require.NoError(t, err)

require.NoError(t, acnt.BuildInvokeTx(context.Background(), &test.InvokeTx, &[]rpc.FunctionCall{test.FnCall}), "Error building Invoke")
test.InvokeTx.Calldata, err = acnt.FmtCalldata([]rpc.FunctionCall{test.FnCall}, test.CairoContractVersion)
require.NoError(t, err)

txHash, err := acnt.TransactionHashInvoke(test.InvokeTx)
err = acnt.SignInvokeTransaction(context.Background(), &test.InvokeTx)
require.NoError(t, err)
require.Equal(t, txHash.String(), test.ExpectedHash.String())

resp, err := acnt.AddInvokeTransaction(context.Background(), test.InvokeTx)
require.Equal(t, err.Error(), test.ExpectedError.Error())
require.Nil(t, resp)
if err != nil {
require.Equal(t, err.Error(), test.ExpectedError.Error())
require.Nil(t, resp)
}

}
}

Expand Down