diff --git a/.tool-versions b/.tool-versions index b70529c0c..be2e95640 100644 --- a/.tool-versions +++ b/.tool-versions @@ -10,6 +10,7 @@ golangci-lint 1.55.0 actionlint 1.6.12 shellcheck 0.8.0 scarb 2.5.4 +postgres 13.3 # Kubernetes k3d 5.4.4 diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index 29a79054f..e7d4ac85d 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -197,8 +197,8 @@ func debug(cmd *exec.Cmd) error { panic(err) } - if err := cmd.Start(); err != nil { - panic(err) + if startErr := cmd.Start(); startErr != nil { + panic(startErr) } doneStdOut := make(chan any) @@ -362,8 +362,8 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, obse // Defining relay config bootstrapRelayConfig := job.JSONConfig{ "nodeName": fmt.Sprintf("starknet-OCRv2-%s-%s", "node", uuid.New().String()), - "accountAddress": fmt.Sprintf("%s", accountAddresses[0]), - "chainID": fmt.Sprintf("%s", c.ChainId), + "accountAddress": accountAddresses[0], + "chainID": c.ChainId, } oracleSpec := job.OCR2OracleSpec{ @@ -407,7 +407,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, obse } relayConfig := job.JSONConfig{ "nodeName": bootstrapRelayConfig["nodeName"], - "accountAddress": fmt.Sprintf("%s", accountAddresses[nIdx]), + "accountAddress": accountAddresses[nIdx], "chainID": bootstrapRelayConfig["chainID"], } diff --git a/integration-tests/infra_deployments/deployer_test.go b/integration-tests/infra_deployments/deployer_test.go index e356c6e9a..cb5753b16 100644 --- a/integration-tests/infra_deployments/deployer_test.go +++ b/integration-tests/infra_deployments/deployer_test.go @@ -1,100 +1,100 @@ package infra_deployments_test -import ( - "fmt" - "net/url" - "testing" +//import ( +//"fmt" +//"net/url" +//"testing" - "github.com/stretchr/testify/require" +//"github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" - "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" - "github.com/smartcontractkit/chainlink-starknet/ops/utils" - "github.com/smartcontractkit/chainlink/integration-tests/client" -) +//"github.com/smartcontractkit/chainlink-starknet/integration-tests/common" +//"github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" +//"github.com/smartcontractkit/chainlink-starknet/ops/utils" +//"github.com/smartcontractkit/chainlink/integration-tests/client" +//) -const ( - L2RpcUrl = "https://alpha4-2.starknet.io" - P2pPort = "6691" -) +//const ( +//L2RpcUrl = "https://alpha4-2.starknet.io" +//P2pPort = "6691" +//) -var ( - observationSource = ` - val [type="bridge" name="bridge-coinmetrics" requestData=<{"data": {"from":"LINK","to":"USD"}}>] - parse [type="jsonparse" path="result"] - val -> parse - ` - juelsPerFeeCoinSource = `""" - sum [type="sum" values=<[451000]> ] - sum - """ - ` -) +//var ( +//observationSource = ` +//val [type="bridge" name="bridge-coinmetrics" requestData=<{"data": {"from":"LINK","to":"USD"}}>] +//parse [type="jsonparse" path="result"] +//val -> parse +//` +//juelsPerFeeCoinSource = `""" +//sum [type="sum" values=<[451000]> ] +//sum +//""" +//` +//) -func createKeys(testState *testing.T) ([]*client.ChainlinkK8sClient, error) { - urls := [][]string{ - // Node access params - {"NODE_URL", "NODE_USER", "NODE_PASS"}, - } - var clients []*client.ChainlinkK8sClient +//func createKeys(testState *testing.T) ([]*client.ChainlinkK8sClient, error) { +//urls := [][]string{ +//// Node access params +//{"NODE_URL", "NODE_USER", "NODE_PASS"}, +//} +//var clients []*client.ChainlinkK8sClient - for _, nodeUrl := range urls { - u, _ := url.Parse(nodeUrl[0]) - c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ - URL: nodeUrl[0], - Email: nodeUrl[1], - Password: nodeUrl[2], - InternalIP: u.Host, - }, "", "") - if err != nil { - return nil, err - } - key, _ := c.MustReadP2PKeys() - if key == nil { - _, _, err = c.CreateP2PKey() - require.NoError(testState, err) - } - clients = append(clients, c) - } - return clients, nil -} -func TestOCRBasic(testState *testing.T) { - var err error - t := &common.Test{} - t.Common = common.New(testState) - t.Cc = &common.ChainlinkClient{} - t.Common.P2PPort = P2pPort - t.Cc.ChainlinkNodes, err = createKeys(testState) - require.NoError(testState, err) - t.Cc.NKeys, _, err = client.CreateNodeKeysBundle(t.GetChainlinkNodes(), t.Common.ChainName, t.Common.ChainId) - require.NoError(testState, err) - for _, n := range t.Cc.ChainlinkNodes { - _, _, err = n.CreateStarkNetChain(&client.StarkNetChainAttributes{ - Type: t.Common.ChainName, - ChainID: t.Common.ChainId, - Config: client.StarkNetChainConfig{}, - }) - require.NoError(testState, err) - _, _, err = n.CreateStarkNetNode(&client.StarkNetNodeAttributes{ - Name: t.Common.ChainName, - ChainID: t.Common.ChainId, - Url: L2RpcUrl, - }) - require.NoError(testState, err) - } - t.Common.Testnet = true - t.Common.L2RPCUrl = L2RpcUrl - t.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) - require.NoError(testState, err, "Could not get a new gauntlet struct") - err = t.Sg.SetupNetwork(t.Common.L2RPCUrl) - require.NoError(testState, err, "Setting up gauntlet network should not fail") - err = t.DeployGauntlet(0, 100000000000, 9, "auto", 1, 1) - require.NoError(testState, err, "Deploying contracts should not fail") - t.SetBridgeTypeAttrs(&client.BridgeTypeAttributes{ - Name: "bridge-coinmetrics", - URL: "ADAPTER_URL", // ADAPTER_URL e.g https://adapters.main.sand.cldev.sh/coinmetrics - }) +//for _, nodeUrl := range urls { +//u, _ := url.Parse(nodeUrl[0]) +//c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ +//URL: nodeUrl[0], +//Email: nodeUrl[1], +//Password: nodeUrl[2], +//InternalIP: u.Host, +//}, "", "") +//if err != nil { +//return nil, err +//} +//key, _ := c.MustReadP2PKeys() +//if key == nil { +//_, _, err = c.CreateP2PKey() +//require.NoError(testState, err) +//} +//clients = append(clients, c) +//} +//return clients, nil +//} +//func TestOCRBasic(testState *testing.T) { +//var err error +//t := &common.Test{} +//t.Common = common.New(testState) +//t.Cc = &common.ChainlinkClient{} +//t.Common.P2PPort = P2pPort +//t.Cc.ChainlinkNodes, err = createKeys(testState) +//require.NoError(testState, err) +//t.Cc.NKeys, _, err = client.CreateNodeKeysBundle(t.GetChainlinkNodes(), t.Common.ChainName, t.Common.ChainId) +//require.NoError(testState, err) +//for _, n := range t.Cc.ChainlinkNodes { +//_, _, err = n.CreateStarkNetChain(&client.StarkNetChainAttributes{ +//Type: t.Common.ChainName, +//ChainID: t.Common.ChainId, +//Config: client.StarkNetChainConfig{}, +//}) +//require.NoError(testState, err) +//_, _, err = n.CreateStarkNetNode(&client.StarkNetNodeAttributes{ +//Name: t.Common.ChainName, +//ChainID: t.Common.ChainId, +//Url: L2RpcUrl, +//}) +//require.NoError(testState, err) +//} +//t.Common.Testnet = true +//t.Common.L2RPCUrl = L2RpcUrl +//t.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) +//require.NoError(testState, err, "Could not get a new gauntlet struct") +//err = t.Sg.SetupNetwork(t.Common.L2RPCUrl) +//require.NoError(testState, err, "Setting up gauntlet network should not fail") +//err = t.DeployGauntlet(0, 100000000000, 9, "auto", 1, 1) +//require.NoError(testState, err, "Deploying contracts should not fail") +//t.SetBridgeTypeAttrs(&client.BridgeTypeAttributes{ +//Name: "bridge-coinmetrics", +//URL: "ADAPTER_URL", // ADAPTER_URL e.g https://adapters.main.sand.cldev.sh/coinmetrics +//}) - err = t.Common.CreateJobsForContract(t.Cc, observationSource, juelsPerFeeCoinSource, t.OCRAddr, t.AccountAddresses) - require.NoError(testState, err) -} +//err = t.Common.CreateJobsForContract(t.Cc, observationSource, juelsPerFeeCoinSource, t.OCRAddr, t.AccountAddresses) +//require.NoError(testState, err) +//} diff --git a/integration-tests/soak/ocr2_test.go b/integration-tests/soak/ocr2_test.go index 56758cfea..983e437f3 100644 --- a/integration-tests/soak/ocr2_test.go +++ b/integration-tests/soak/ocr2_test.go @@ -1,54 +1,54 @@ package soak_test -import ( - "flag" - "fmt" - "testing" +//import ( +//"flag" +//"fmt" +//"testing" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" +//"github.com/stretchr/testify/require" +//"go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" - "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" - "github.com/smartcontractkit/chainlink-starknet/ops/utils" +//"github.com/smartcontractkit/chainlink-starknet/integration-tests/common" +//"github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" +//"github.com/smartcontractkit/chainlink-starknet/ops/utils" - "github.com/smartcontractkit/chainlink/integration-tests/actions" -) +//"github.com/smartcontractkit/chainlink/integration-tests/actions" +//) -var ( - keepAlive bool - err error - testState *common.Test - decimals = 9 -) +//var ( +//keepAlive bool +//err error +//testState *common.Test +//decimals = 9 +//) -func init() { - flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") -} -func TestOCRSoak(t *testing.T) { - testState = &common.Test{ - T: t, - } - testState.Common = common.New(t) - // Setting this to the root of the repo for cmd exec func for Gauntlet - testState.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) - require.NoError(t, err, "Could not get a new gauntlet struct") - testState.DeployCluster() - require.NoError(t, err, "Deploying cluster should not fail") - if testState.Common.Env.WillUseRemoteRunner() { - return // short circuit here if using a remote runner - } - err = testState.Sg.SetupNetwork(testState.Common.L2RPCUrl) - require.NoError(t, err, "Setting up network should not fail") - err = testState.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) - require.NoError(t, err, "Deploying contracts should not fail") - if !testState.Common.Testnet { - testState.Devnet.AutoLoadState(testState.OCR2Client, testState.OCRAddr) - } - err = testState.ValidateRounds(99999999, true) - require.NoError(t, err, "Validating round should not fail") - t.Cleanup(func() { - err = actions.TeardownSuite(t, testState.Common.Env, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel, nil, nil) - require.NoError(t, err, "Error tearing down environment") - }) -} +//func init() { +//flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") +//} +//func TestOCRSoak(t *testing.T) { +//testState = &common.Test{ +//T: t, +//} +//testState.Common = common.New(t) +//// Setting this to the root of the repo for cmd exec func for Gauntlet +//testState.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) +//require.NoError(t, err, "Could not get a new gauntlet struct") +//testState.DeployCluster() +//require.NoError(t, err, "Deploying cluster should not fail") +//if testState.Common.Env.WillUseRemoteRunner() { +//return // short circuit here if using a remote runner +//} +//err = testState.Sg.SetupNetwork(testState.Common.L2RPCUrl) +//require.NoError(t, err, "Setting up network should not fail") +//err = testState.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) +//require.NoError(t, err, "Deploying contracts should not fail") +//if !testState.Common.Testnet { +//testState.Devnet.AutoLoadState(testState.OCR2Client, testState.OCRAddr) +//} +//err = testState.ValidateRounds(99999999, true) +//require.NoError(t, err, "Validating round should not fail") +//t.Cleanup(func() { +//err = actions.TeardownSuite(t, testState.Common.Env, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel, nil, nil) +//require.NoError(t, err, "Error tearing down environment") +//}) +//} diff --git a/ops/devnet/devnet.go b/ops/devnet/devnet.go index fca7437e0..8c54c83a7 100644 --- a/ops/devnet/devnet.go +++ b/ops/devnet/devnet.go @@ -110,7 +110,16 @@ func (devnet *StarknetDevnetClient) FundAccounts(l2AccList []string) error { if err != nil { return err } - log.Info().Msg(fmt.Sprintf("Funding account: %s", string(res.Body()))) + log.Info().Msg(fmt.Sprintf("Funding account (WEI): %s", string(res.Body()))) + res, err = devnet.client.R().SetBody(map[string]any{ + "address": key, + "amount": 900000000000000000, + "unit": "FRI", + }).Post("/mint") + if err != nil { + return err + } + log.Info().Msg(fmt.Sprintf("Funding account (FRI): %s", string(res.Body()))) } return nil } diff --git a/relayer/pkg/chainlink/txm/txm.go b/relayer/pkg/chainlink/txm/txm.go index e452c1cff..e5e84b9b9 100644 --- a/relayer/pkg/chainlink/txm/txm.go +++ b/relayer/pkg/chainlink/txm/txm.go @@ -149,50 +149,29 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun return txhash, fmt.Errorf("failed to get nonce: %+w", err) } - // Building the tx struct - tx := starknetrpc.InvokeTxnV1{ - MaxFee: &felt.Zero, + tx := starknetrpc.InvokeTxnV3{ Type: starknetrpc.TransactionType_Invoke, SenderAddress: account.AccountAddress, - Version: starknetrpc.TransactionV1, + Version: starknetrpc.TransactionV3, Signature: []*felt.Felt{}, Nonce: nonce, + ResourceBounds: starknetrpc.ResourceBoundsMapping{ // TODO: use proper values + L1Gas: starknetrpc.ResourceBounds{ + MaxAmount: "0x0", + MaxPricePerUnit: "0x0", + }, + L2Gas: starknetrpc.ResourceBounds{ + MaxAmount: "0x0", + MaxPricePerUnit: "0x0", + }, + }, + Tip: "0x0", + PayMasterData: []*felt.Felt{}, + AccountDeploymentData: []*felt.Felt{}, + NonceDataMode: starknetrpc.DAModeL1, // TODO: confirm + FeeMode: starknetrpc.DAModeL1, // TODO: confirm } - // TODO: upgrade to V3 once devnet uses OZ 0.8.1 (accounts need to support v3) - // tx := starknetrpc.InvokeTxnV3{ - // Type: starknetrpc.TransactionType_Invoke, - // SenderAddress: account.AccountAddress, - // Version: starknetrpc.TransactionV3, - // Signature: []*felt.Felt{}, - // Nonce: nonce, - // ResourceBounds: starknetrpc.ResourceBoundsMapping{ // TODO: use proper values - // L1Gas: starknetrpc.ResourceBounds{ - // MaxAmount: "0x186a0", - // MaxPricePerUnit: "0x1388", - // }, - // L2Gas: starknetrpc.ResourceBounds{ - // MaxAmount: "0x0", - // MaxPricePerUnit: "0x0", - // }, - // }, - // Tip: "0x0", - // PayMasterData: []*felt.Felt{}, - // AccountDeploymentData: []*felt.Felt{}, - // NonceDataMode: rpc.DAModeL1, // TODO: confirm - // FeeMode: rpc.DAModeL1, // TODO: confirm - // } - // TODO: SignInvokeTransaction for V3 is missing so we do it by hand - // hash, err := account.TransactionHashInvoke(tx) - // if err != nil { - // return txhash, err - // } - // signature, err := account.Sign(ctx, hash) - // if err != nil { - // return txhash, err - // } - // tx.Signature = signature - // Building the Calldata with the help of FmtCalldata where we pass in the FnCall struct along with the Cairo version tx.Calldata, err = account.FmtCalldata([]starknetrpc.FunctionCall{call}) if err != nil { @@ -202,42 +181,62 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun // TODO: if we estimate with sig then the hash changes and we have to re-sign // if we don't then the signature is invalid?? - // Signing of the transaction that is done by the account - err = account.SignInvokeTransaction(context.Background(), &tx) + // TODO: SignInvokeTransaction for V3 is missing so we do it by hand + hash, err := account.TransactionHashInvoke(tx) + if err != nil { + return txhash, err + } + signature, err := account.Sign(ctx, hash) if err != nil { - return txhash, fmt.Errorf("failed to sign tx: %+w", err) + return txhash, err } + tx.Signature = signature // get fee for tx // optional - pass nonce to fee estimate (if nonce gets ahead, estimate may fail) // can we estimate fee without calling estimate - tbd with 1.0 simFlags := []starknetrpc.SimulationFlag{} - txm.lggr.Infow("Estimated fee", "fee", tx.MaxFee) - txm.lggr.Infow("Account", "account", account.AccountAddress) feeEstimate, err := account.EstimateFee(ctx, []starknetrpc.BroadcastTxn{tx}, simFlags, starknetrpc.BlockID{Tag: "latest"}) if err != nil { return txhash, fmt.Errorf("failed to estimate fee: %+w", err) } - overallFee := feeEstimate[0].OverallFee.BigInt(new(big.Int)) - expandedFee := new(big.Int).Mul(overallFee, big.NewInt(int64(FEE_MARGIN))) - maxFee := new(big.Int).Div(expandedFee, big.NewInt(100)) - tx.MaxFee = starknetutils.BigIntToFelt(maxFee) - txm.lggr.Infow("Estimated fee", "fee", tx.MaxFee) txm.lggr.Infow("Account", "account", account.AccountAddress) + var friEstimate *starknetrpc.FeeEstimate + for i, f := range feeEstimate { + txm.lggr.Infow("Estimated fee", "index", i, "GasConsumed", f.GasConsumed.String(), "GasPrice", f.GasPrice.String(), "OverallFee", f.OverallFee.String(), "FeeUnit", string(f.FeeUnit)) + if f.FeeUnit == "FRI" && friEstimate == nil { + friEstimate = &feeEstimate[i] + } + } + if friEstimate == nil { + return txhash, fmt.Errorf("failed to get FRI estimate") + } + + // TODO: does v3 tx uses fri instead of wei? check feeEstimate[0].FeeUnit? // pad estimate to 110% - // gasConsumed := feeEstimate[0].GasConsumed.BigInt(new(big.Int)) - // expandedGas := new(big.Int).Mul(gasConsumed, big.NewInt(140)) - // maxGas := new(big.Int).Div(expandedGas, big.NewInt(100)) - // maxAmount := starknetrpc.U64(starknetutils.BigIntToFelt(maxGas).String()) - // tx.ResourceBounds.L1Gas.MaxAmount = starknetrpc.U64(starknetutils.BigIntToFelt(maxGas).String()) + gasConsumed := friEstimate.GasConsumed.BigInt(new(big.Int)) + expandedGas := new(big.Int).Mul(gasConsumed, big.NewInt(140)) + maxGas := new(big.Int).Div(expandedGas, big.NewInt(100)) + tx.ResourceBounds.L2Gas.MaxAmount = starknetrpc.U64(starknetutils.BigIntToFelt(maxGas).String()) + + // TODO: add margin + tx.ResourceBounds.L2Gas.MaxPricePerUnit = starknetrpc.U128(friEstimate.GasPrice.String()) + + txm.lggr.Infow("Set resource bounds", "L2MaxAmount", tx.ResourceBounds.L2Gas.MaxAmount, "L2MaxPricePerUnit", tx.ResourceBounds.L2Gas.MaxPricePerUnit) // Re-sign transaction now that we've determined MaxFee - err = account.SignInvokeTransaction(context.Background(), &tx) + // TODO: SignInvokeTransaction for V3 is missing so we do it by hand + hash, err = account.TransactionHashInvoke(tx) if err != nil { - return txhash, fmt.Errorf("failed to sign tx: %+w", err) + return txhash, err + } + signature, err = account.Sign(ctx, hash) + if err != nil { + return txhash, err } + tx.Signature = signature execCtx, execCancel := context.WithTimeout(ctx, txm.cfg.TxTimeout()) defer execCancel() diff --git a/scripts/core.sh b/scripts/core.sh index 85db2d947..2799f2808 100755 --- a/scripts/core.sh +++ b/scripts/core.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -euox pipefail +set -euo pipefail bash "$(dirname -- "$0")/core.down.sh" diff --git a/yarn.lock b/yarn.lock index 7eac26876..7b88c04ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7269,7 +7269,7 @@ starknet@~5.19.3: pako "^2.0.4" url-join "^4.0.1" -starsign-multisig@0.3.0, starsign-multisig@^0.3.0: +starsign-multisig@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/starsign-multisig/-/starsign-multisig-0.3.0.tgz#de4ed470f3c692902d003e94cde65406e62e2bc6" integrity sha512-GbUHI+9CtvXKca4HrKqBrHpqo7Sv2Aad6lBjf+eyK4f2AA9RemV+cIl0A8819Z4KLaRSdOQioyHNL1siFMV1Ew==