Skip to content

Commit

Permalink
Support requesting faucet funds for address
Browse files Browse the repository at this point in the history
This adds support for requesting faucet funds for a given address.
This will be rate limited by the address via the smart contract
that backs the faucet and via the API key (1 request every 24 hours).

This may be able to be loosened for base-sepolia via the Platform API,
but those are the current rate limit parameters.
  • Loading branch information
alex-stone committed May 2, 2024
1 parent 97b67d8 commit d0c0155
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Faucet
- Trade
- Individual private key export
- Allow disabling debug tracing
Expand All @@ -18,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Coinbase.configure_from_file
- Faucet

## [0.0.2] - 2024-05-01

Expand Down
6 changes: 5 additions & 1 deletion lib/coinbase.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# frozen_string_literal: true

module Coinbase; end

require_relative 'coinbase/client'
require_relative 'coinbase/address'
require_relative 'coinbase/asset'
require_relative 'coinbase/authenticator'
require_relative 'coinbase/balance_map'
require_relative 'coinbase/client'
require_relative 'coinbase/constants'
require_relative 'coinbase/faucet_transaction'
require_relative 'coinbase/middleware'
require_relative 'coinbase/network'
require_relative 'coinbase/transfer'
Expand All @@ -16,6 +19,7 @@
# The Coinbase SDK.
module Coinbase
class InvalidConfiguration < StandardError; end
class FaucetLimitReached < StandardError; end

# Returns the configuration object.
# @return [Configuration] the configuration object
Expand Down
16 changes: 16 additions & 0 deletions lib/coinbase/address.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative 'client'
require_relative 'balance_map'
require_relative 'constants'
require_relative 'wallet'
Expand Down Expand Up @@ -123,6 +124,21 @@ def to_s
address_id
end

# Requests funds for the address from the faucet and returns the faucet transaction.
# This is only supported on testnet networks.
# @return [Coinbase::FaucetTransaction] The successful faucet transaction
# @raise [Coinbase::FaucetLimitReached] If the faucet limit has been reached for the address or user.
# @raise [Coinbase::Client::ApiError] If an unexpected error occurs while requesting faucet funds.
def faucet
Coinbase::FaucetTransaction.new(addresses_api.request_faucet_funds(wallet_id, address_id))
rescue Coinbase::Client::ApiError => e
if e.code == 'faucet_limit_reached'
fail ::Coinbase::FaucetLimitReached, e.message
end

fail e
end

private

# Normalizes the amount of the Asset to send to the atomic unit.
Expand Down
15 changes: 15 additions & 0 deletions lib/coinbase/faucet_transaction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Coinbase
class FaucetTransaction
def initialize(model)
@model = model
end

attr_reader :model

def transaction_hash
model.transaction_hash
end
end
end
59 changes: 59 additions & 0 deletions spec/unit/coinbase/address_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,65 @@
end
end

describe '#faucet' do
let(:request) { double('Request', transaction: transaction) }
let(:tx_hash) { '0xdeadbeef' }
let(:faucet_tx) do
instance_double('Coinbase::Client::FaucetTransaction', transaction_hash: tx_hash)
end

context 'when the request is successful' do
subject(:faucet_response) { address.faucet }

before do
expect(addresses_api)
.to receive(:request_faucet_funds)
.with(wallet_id, address_id)
.and_return(faucet_tx)
end

it 'requests funds from the faucet and returns the faucet transaction' do
expect(faucet_response).to be_a(Coinbase::FaucetTransaction)
expect(faucet_response.transaction_hash).to eq(tx_hash)
end
end

context 'when the request is unsuccesful' do
before do
expect(addresses_api)
.to receive(:request_faucet_funds)
.with(wallet_id, address_id)
.and_raise(api_error)
end

context 'when the faucet limit is reached' do
let(:api_error) do
Coinbase::Client::ApiError.new(
code: 'faucet_limit_reached',
message: 'failed to claim funds - address likely has already claimed in the past 24 hours',
)
end

it 'raises a FaucetLimitReached error' do
expect { address.faucet }.to raise_error(::Coinbase::FaucetLimitReached, api_error.message)
end
end

context 'when the request fails unexpectedly' do
let(:api_error) do
Coinbase::Client::ApiError.new(
code: 'internal_error',
message: 'Unxpected faucet error'
)
end

it 'raises the api error' do
expect { address.faucet }.to raise_error(api_error)
end
end
end
end

describe '#to_s' do
it 'returns the address as a string' do
expect(address.to_s).to eq(address_id)
Expand Down

0 comments on commit d0c0155

Please sign in to comment.