diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index 19999115555..95c98ad1bbd 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -906,6 +906,22 @@ func main() { tx, err := coordinator.CancelSubscription(e.Owner, parseSubID(*subID), e.Owner.From) helpers.PanicErr(err) helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID) + case "eoa-fund-sub-with-native-token": + fund := flag.NewFlagSet("eoa-fund-sub-with-native-token", flag.ExitOnError) + coordinatorAddress := fund.String("coordinator-address", "", "coordinator address") + amountStr := fund.String("amount", "", "amount to fund in wei") + subID := fund.String("sub-id", "", "sub-id") + helpers.ParseArgs(fund, os.Args[2:], "coordinator-address", "amount", "sub-id") + amount, s := big.NewInt(0).SetString(*amountStr, 10) + if !s { + panic(fmt.Sprintf("failed to parse top up amount '%s'", *amountStr)) + } + coordinator, err := vrf_coordinator_v2plus.NewVRFCoordinatorV2Plus(common.HexToAddress(*coordinatorAddress), e.Ec) + helpers.PanicErr(err) + e.Owner.Value = amount + tx, err := coordinator.FundSubscriptionWithEth(e.Owner, parseSubID(*subID)) + helpers.PanicErr(err) + helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID) case "eoa-fund-sub": fund := flag.NewFlagSet("eoa-fund-sub", flag.ExitOnError) coordinatorAddress := fund.String("coordinator-address", "", "coordinator address") diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_constants/constants.go b/integration-tests/actions/vrfv2plus/vrfv2plus_constants/constants.go index 930c12b2166..93850a6ab55 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_constants/constants.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_constants/constants.go @@ -6,17 +6,17 @@ import ( ) var ( - LinkEthFeedResponse = big.NewInt(1e18) - MinimumConfirmations = uint16(3) - RandomnessRequestCountPerRequest = uint16(1) - VRFSubscriptionFundingAmountLink = big.NewInt(100) - ChainlinkNodeFundingAmountEth = big.NewFloat(0.1) - NumberOfWords = uint32(3) - MaxGasPriceGWei = 1000 - CallbackGasLimit = uint32(1000000) - MaxGasLimitVRFCoordinatorConfig = uint32(2.5e6) - StalenessSeconds = uint32(86400) - GasAfterPaymentCalculation = uint32(33825) + LinkEthFeedResponse = big.NewInt(1e18) + MinimumConfirmations = uint16(3) + RandomnessRequestCountPerRequest = uint16(1) + VRFSubscriptionFundingAmountLink = big.NewInt(100) + VRFSubscriptionFundingAmountNativeToken = big.NewInt(1) + ChainlinkNodeFundingAmountEth = big.NewFloat(0.1) + NumberOfWords = uint32(3) + CallbackGasLimit = uint32(1000000) + MaxGasLimitVRFCoordinatorConfig = uint32(2.5e6) + StalenessSeconds = uint32(86400) + GasAfterPaymentCalculation = uint32(33825) VRFCoordinatorV2PlusFeeConfig = vrf_coordinator_v2plus.VRFCoordinatorV2PlusFeeConfig{ FulfillmentFlatFeeLinkPPM: 500, diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index fce5f54966b..b149fc4fdc9 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -7,8 +7,11 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_constants" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" "math/big" ) @@ -27,6 +30,19 @@ var ( ErrSendingLinkToken = "error sending Link token" ErrCreatingVRFv2PlusJob = "error creating VRFv2Plus job" ErrParseJob = "error parsing job definition" + + ErrDeployVRFV2PlusContracts = "error deploying VRFV2Plus contracts" + ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract" + ErrCreateVRFSubscription = "error creating VRF Subscription" + ErrFindSubID = "error finding created subscription ID" + ErrAddConsumerToSub = "error adding consumer to VRF Subscription" + ErrFundSubWithNativeToken = "error funding subscription with native token" + ErrSetLinkETHLinkFeed = "error setting Link and ETH/LINK feed for VRF Coordinator contract" + ErrFundSubWithLinkToken = "error funding subscription with Link tokens" + ErrCreateVRFV2PlusJobs = "error creating VRF V2 Plus Jobs" + ErrGetPrimaryKey = "error getting primary ETH key address" + ErrRestartCLNode = "error restarting CL node" + ErrWaitTXsComplete = "error waiting for TXs to complete" ) func DeployVRFV2PlusContracts( @@ -47,7 +63,7 @@ func DeployVRFV2PlusContracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, err + return nil, errors.Wrap(err, ErrWaitTXsComplete) } return &VRFV2PlusContracts{coordinator, bhs, loadTestConsumer}, nil } @@ -57,8 +73,8 @@ func CreateVRFV2PlusJobs( coordinator contracts.VRFCoordinatorV2Plus, c blockchain.EVMClient, minIncomingConfirmations uint16, -) ([]VRFV2PlusJobInfo, error) { - jobInfo := make([]VRFV2PlusJobInfo, 0) +) ([]*VRFV2PlusJobInfo, error) { + jobInfo := make([]*VRFV2PlusJobInfo, 0) for _, chainlinkNode := range chainlinkNodes { vrfKey, err := chainlinkNode.MustCreateVRFKey() if err != nil { @@ -99,7 +115,7 @@ func CreateVRFV2PlusJobs( if err != nil { return nil, errors.Wrap(err, ErrCreatingProvingKeyHash) } - ji := VRFV2PlusJobInfo{ + ji := &VRFV2PlusJobInfo{ Job: job, VRFKey: vrfKey, EncodedProvingKey: provingKey, @@ -140,3 +156,100 @@ func FundVRFCoordinatorV2PlusSubscription(linkToken contracts.LinkToken, coordin } return chainClient.WaitForEvents() } + +func SetupVRFV2PlusEnvironment( + env *test_env.CLClusterTestEnv, + linkAddress contracts.LinkToken, + mockETHLinkFeedAddress contracts.MockETHLINKFeed, +) (*test_env.CLClusterTestEnv, *VRFV2PlusContracts, *big.Int, *VRFV2PlusJobInfo, error) { + + vrfv2PlusContracts, err := DeployVRFV2PlusContracts(env.ContractDeployer, env.EVMClient) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrDeployVRFV2PlusContracts) + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + } + + err = vrfv2PlusContracts.Coordinator.SetConfig( + vrfv2plus_constants.MinimumConfirmations, + vrfv2plus_constants.MaxGasLimitVRFCoordinatorConfig, + vrfv2plus_constants.StalenessSeconds, + vrfv2plus_constants.GasAfterPaymentCalculation, + vrfv2plus_constants.LinkEthFeedResponse, + vrfv2plus_constants.VRFCoordinatorV2PlusFeeConfig, + ) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrSetVRFCoordinatorConfig) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + } + + err = vrfv2PlusContracts.Coordinator.CreateSubscription() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrCreateVRFSubscription) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + } + + subID, err := vrfv2PlusContracts.Coordinator.FindSubscriptionID() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrFindSubID) + } + + err = vrfv2PlusContracts.Coordinator.AddConsumer(subID, vrfv2PlusContracts.LoadTestConsumer.Address()) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrAddConsumerToSub) + } + + //Native Billing + err = vrfv2PlusContracts.Coordinator.FundSubscriptionWithEth(subID, big.NewInt(0).Mul(vrfv2plus_constants.VRFSubscriptionFundingAmountNativeToken, big.NewInt(1e18))) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrFundSubWithNativeToken) + } + + //Link Billing + err = vrfv2PlusContracts.Coordinator.SetLINKAndLINKETHFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrSetLinkETHLinkFeed) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + } + err = FundVRFCoordinatorV2PlusSubscription(linkAddress, vrfv2PlusContracts.Coordinator, env.EVMClient, subID, vrfv2plus_constants.VRFSubscriptionFundingAmountLink) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrFundSubWithLinkToken) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + } + + vrfV2PlusJobs, err := CreateVRFV2PlusJobs(env.GetAPIs(), vrfv2PlusContracts.Coordinator, env.EVMClient, vrfv2plus_constants.MinimumConfirmations) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrCreateVRFV2PlusJobs) + } + + // this part is here because VRFv2 can work with only a specific key + // [[EVM.KeySpecific]] + // Key = '...' + addr, err := env.CLNodes[0].API.PrimaryEthAddress() + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrGetPrimaryKey) + } + nodeConfig := node.NewConfig(env.CLNodes[0].NodeConfig, + node.WithVRFv2EVMEstimator(addr), + ) + err = env.CLNodes[0].Restart(nodeConfig) + if err != nil { + return nil, nil, nil, nil, errors.Wrap(err, ErrRestartCLNode) + } + return env, vrfv2PlusContracts, subID, vrfV2PlusJobs[0], nil +} diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 0cd9778a99f..edc89d8cd58 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -72,9 +72,11 @@ type VRFCoordinatorV2Plus interface { HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) CreateSubscription() error AddConsumer(subId *big.Int, consumerAddress string) error + FundSubscriptionWithEth(subId *big.Int, nativeTokenAmount *big.Int) error Address() string GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2plus.GetSubscription, error) FindSubscriptionID() (*big.Int, error) + WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2plus.VRFCoordinatorV2PlusRandomWordsFulfilled, error) } type VRFConsumer interface { diff --git a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go index c38a6ea3ac7..12e33cac48b 100644 --- a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "math/big" + "time" ) type EthereumVRFCoordinatorV2Plus struct { @@ -150,6 +151,22 @@ func (v *EthereumVRFCoordinatorV2Plus) AddConsumer(subId *big.Int, consumerAddre return v.client.ProcessTransaction(tx) } +func (v *EthereumVRFCoordinatorV2Plus) FundSubscriptionWithEth(subId *big.Int, nativeTokenAmount *big.Int) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + opts.Value = nativeTokenAmount + tx, err := v.coordinator.FundSubscriptionWithEth( + opts, + subId, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + func (v *EthereumVRFCoordinatorV2Plus) FindSubscriptionID() (*big.Int, error) { owner := v.client.GetDefaultWallet().Address() subscriptionIterator, err := v.coordinator.FilterSubscriptionCreated( @@ -167,6 +184,26 @@ func (v *EthereumVRFCoordinatorV2Plus) FindSubscriptionID() (*big.Int, error) { return subscriptionIterator.Event.SubId, nil } +func (v *EthereumVRFCoordinatorV2Plus) WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2plus.VRFCoordinatorV2PlusRandomWordsFulfilled, error) { + randomWordsFulfilledEventsChannel := make(chan *vrf_coordinator_v2plus.VRFCoordinatorV2PlusRandomWordsFulfilled) + subscription, err := v.coordinator.WatchRandomWordsFulfilled(nil, randomWordsFulfilledEventsChannel, requestID, subID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for RandomWordsFulfilled event") + case randomWordsFulfilledEvent := <-randomWordsFulfilledEventsChannel: + return randomWordsFulfilledEvent, nil + } + } +} + func (v *EthereumVRFv2PlusLoadTestConsumer) Address() string { return v.address.Hex() } diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 8ed93a9960c..b3a99f8ca4e 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -2,22 +2,19 @@ package smoke import ( "context" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_constants" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "math/big" "testing" "time" - "github.com/onsi/gomega" "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" ) -func TestVRFv2PlusBasic(t *testing.T) { +func TestVRFv2PlusBilling(t *testing.T) { t.Parallel() l := utils.GetTestLogger(t) @@ -26,94 +23,129 @@ func TestVRFv2PlusBasic(t *testing.T) { WithCLNodes(1). WithFunding(vrfv2plus_constants.ChainlinkNodeFundingAmountEth). Build() - require.NoError(t, err) + + require.NoError(t, err, "error creating test env") + env.ParallelTransactions(true) mockETHLinkFeedAddress, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, vrfv2plus_constants.LinkEthFeedResponse) - require.NoError(t, err) + require.NoError(t, err, "error deploying mock ETH/LINK feed") + linkAddress, err := actions.DeployLINKToken(env.ContractDeployer) - require.NoError(t, err) - vrfv2PlusContracts, err := vrfv2plus.DeployVRFV2PlusContracts(env.ContractDeployer, env.EVMClient) - require.NoError(t, err) - - err = env.EVMClient.WaitForEvents() - require.NoError(t, err) - - err = vrfv2PlusContracts.Coordinator.SetLINKAndLINKETHFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) - require.NoError(t, err) - - err = vrfv2PlusContracts.Coordinator.SetConfig( - vrfv2plus_constants.MinimumConfirmations, - vrfv2plus_constants.MaxGasLimitVRFCoordinatorConfig, - vrfv2plus_constants.StalenessSeconds, - vrfv2plus_constants.GasAfterPaymentCalculation, - vrfv2plus_constants.LinkEthFeedResponse, - vrfv2plus_constants.VRFCoordinatorV2PlusFeeConfig, - ) - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err) - - err = vrfv2PlusContracts.Coordinator.CreateSubscription() - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err) - - subID, err := vrfv2PlusContracts.Coordinator.FindSubscriptionID() - require.NoError(t, err) - - err = vrfv2PlusContracts.Coordinator.AddConsumer(subID, vrfv2PlusContracts.LoadTestConsumer.Address()) - require.NoError(t, err) - - err = vrfv2plus.FundVRFCoordinatorV2PlusSubscription(linkAddress, vrfv2PlusContracts.Coordinator, env.EVMClient, subID, vrfv2plus_constants.VRFSubscriptionFundingAmountLink) - require.NoError(t, err) - - vrfV2jobs, err := vrfv2plus.CreateVRFV2PlusJobs(env.GetAPIs(), vrfv2PlusContracts.Coordinator, env.EVMClient, vrfv2plus_constants.MinimumConfirmations) - require.NoError(t, err) - - // this part is here because VRFv2 can work with only a specific key - // [[EVM.KeySpecific]] - // Key = '...' - addr, err := env.CLNodes[0].API.PrimaryEthAddress() - require.NoError(t, err) - nodeConfig := node.NewConfig(env.CLNodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr), - ) - err = env.CLNodes[0].Restart(nodeConfig) - require.NoError(t, err) - - // test and assert - err = vrfv2PlusContracts.LoadTestConsumer.RequestRandomness( - vrfV2jobs[0].KeyHash, - subID, - vrfv2plus_constants.MinimumConfirmations, - vrfv2plus_constants.CallbackGasLimit, - false, - vrfv2plus_constants.NumberOfWords, - vrfv2plus_constants.RandomnessRequestCountPerRequest, - ) - require.NoError(t, err) - - gom := gomega.NewGomegaWithT(t) - timeout := time.Minute * 1 - var lastRequestID *big.Int - gom.Eventually(func(g gomega.Gomega) { - jobRuns, err := env.CLNodes[0].API.MustReadRunsByJob(vrfV2jobs[0].Job.Data.ID) - g.Expect(err).ShouldNot(gomega.HaveOccurred()) - g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically("==", 1)) - lastRequestID, err = vrfv2PlusContracts.LoadTestConsumer.GetLastRequestId(context.Background()) - l.Debug().Interface("Last Request ID", lastRequestID).Msg("Last Request ID Received") - - g.Expect(err).ShouldNot(gomega.HaveOccurred()) - status, err := vrfv2PlusContracts.LoadTestConsumer.GetRequestStatus(context.Background(), lastRequestID) - g.Expect(err).ShouldNot(gomega.HaveOccurred()) - g.Expect(status.Fulfilled).Should(gomega.BeTrue()) + require.NoError(t, err, "error deploying LINK contract") + + env, vrfv2PlusContracts, subID, job, err := vrfv2plus.SetupVRFV2PlusEnvironment(env, linkAddress, mockETHLinkFeedAddress) + require.NoError(t, err, "error setting up VRF v2 Plus env") + + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + + l.Debug(). + Interface("Juels Balance", subscription.Balance). + Interface("Native Token Balance", subscription.EthBalance). + Interface("Subscription ID", subID). + Msg("Subscription Data") + + t.Run("VRFV2 Plus With Link Billing", func(t *testing.T) { + var isNativeBilling = false + subBalanceBeforeRequest := subscription.Balance + + jobRunsBeforeTest, err := env.CLNodes[0].API.MustReadRunsByJob(job.Job.Data.ID) + + // test and assert + err = vrfv2PlusContracts.LoadTestConsumer.RequestRandomness( + job.KeyHash, + subID, + vrfv2plus_constants.MinimumConfirmations, + vrfv2plus_constants.CallbackGasLimit, + isNativeBilling, + vrfv2plus_constants.NumberOfWords, + vrfv2plus_constants.RandomnessRequestCountPerRequest, + ) + require.NoError(t, err, "error requesting randomness") + + randomWordsFulfilledEvent, err := vrfv2PlusContracts.Coordinator.WaitForRandomWordsFulfilledEvent([]*big.Int{subID}, nil, time.Minute*2) + require.NoError(t, err, "error waiting for RandomWordsFulfilled event") + + l.Debug(). + Interface("Total Payment in Juels", randomWordsFulfilledEvent.Payment). + Interface("TX Hash", randomWordsFulfilledEvent.Raw.TxHash). + Interface("Subscription ID", randomWordsFulfilledEvent.SubID). + Interface("Request ID", randomWordsFulfilledEvent.RequestId). + Bool("Success", randomWordsFulfilledEvent.Success). + Msg("Randomness Fulfillment TX metadata") + + expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) + subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + subBalanceAfterRequest := subscription.Balance + require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) + + jobRuns, err := env.CLNodes[0].API.MustReadRunsByJob(job.Job.Data.ID) + require.NoError(t, err, "error reading job runs") + require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) + + status, err := vrfv2PlusContracts.LoadTestConsumer.GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) + l.Debug().Interface("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") + + require.Equal(t, vrfv2plus_constants.NumberOfWords, uint32(len(status.RandomWords))) + for _, w := range status.RandomWords { + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, w.Cmp(big.NewInt(0)), 1, "Expected the VRF job give an answer bigger than 0") + } + }) + + t.Run("VRFV2 Plus With Native Billing", func(t *testing.T) { + var isNativeBilling = true + subNativeTokenBalanceBeforeRequest := subscription.EthBalance + + jobRunsBeforeTest, err := env.CLNodes[0].API.MustReadRunsByJob(job.Job.Data.ID) + + // test and assert + err = vrfv2PlusContracts.LoadTestConsumer.RequestRandomness( + job.KeyHash, + subID, + vrfv2plus_constants.MinimumConfirmations, + vrfv2plus_constants.CallbackGasLimit, + isNativeBilling, + vrfv2plus_constants.NumberOfWords, + vrfv2plus_constants.RandomnessRequestCountPerRequest, + ) + require.NoError(t, err, "error requesting randomness") + + randomWordsFulfilledEvent, err := vrfv2PlusContracts.Coordinator.WaitForRandomWordsFulfilledEvent([]*big.Int{subID}, nil, time.Minute*2) + require.NoError(t, err, "error waiting for RandomWordsFulfilled event") + + l.Debug(). + Interface("Total Payment in Wei", randomWordsFulfilledEvent.Payment). + Interface("TX Hash", randomWordsFulfilledEvent.Raw.TxHash). + Interface("Subscription ID", randomWordsFulfilledEvent.SubID). + Interface("Request ID", randomWordsFulfilledEvent.RequestId). + Bool("Success", randomWordsFulfilledEvent.Success). + Msg("Randomness Fulfillment TX metadata") + + expectedSubBalanceWei := new(big.Int).Sub(subNativeTokenBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) + subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err) + subBalanceAfterRequest := subscription.EthBalance + require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) + + jobRuns, err := env.CLNodes[0].API.MustReadRunsByJob(job.Job.Data.ID) + require.NoError(t, err, "error reading job runs") + require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) + + status, err := vrfv2PlusContracts.LoadTestConsumer.GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) l.Debug().Interface("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - g.Expect(err).ShouldNot(gomega.HaveOccurred()) + require.Equal(t, vrfv2plus_constants.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { - l.Info().Uint64("Output", w.Uint64()).Msg("Randomness fulfilled") - g.Expect(w.Uint64()).Should(gomega.BeNumerically(">", 0), "Expected the VRF job give an answer bigger than 0") + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, w.Cmp(big.NewInt(0)), 1, "Expected the VRF job give an answer bigger than 0") } - }, timeout, "1s").Should(gomega.Succeed()) + }) + }