From cd1391ebaf24e6508d5f83e3eb7c5dc84ac8a088 Mon Sep 17 00:00:00 2001 From: Alex Stone Date: Mon, 18 Nov 2024 13:35:58 -0800 Subject: [PATCH] feat: Execute a fund quote This makes it so that after you generate a fund quote and verify the amounts and fees make sense to you, you can execute that quote. Note: If it has been a while the quote may expire and we will return an error indicating that to you. --- CHANGELOG.md | 3 +++ lib/coinbase/fund_operation.rb | 20 +++++++++++++---- lib/coinbase/fund_quote.rb | 20 +++++++++++++++++ spec/unit/coinbase/fund_quote_spec.rb | 31 +++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c00013e7..4c8791e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +* Add support for funding wallets (Alpha feature release) + * Must reach out to CDP SDK Discord channel to be considered for this feature. + ## [0.10.0] - 2024-10-31 - Include ERC20 and ERC721 token transfer information into transaction content. - Add support for reading from smart contracts. diff --git a/lib/coinbase/fund_operation.rb b/lib/coinbase/fund_operation.rb index 26bcfa8f..40f06b87 100644 --- a/lib/coinbase/fund_operation.rb +++ b/lib/coinbase/fund_operation.rb @@ -25,14 +25,17 @@ module Status class << self # Creates a new Fund Operation object. + # This takes an optional FundQuote object that can be used to lock in the rate and fees. + # Without an explicit quote, we will use the current rate and fees. # @param address_id [String] The Address ID of the sending Address # @param wallet_id [String] The Wallet ID of the sending Wallet # @param amount [BigDecimal] The amount of the Asset to send # @param network [Coinbase::Network, Symbol] The Network or Network ID of the Asset # @param asset_id [Symbol] The Asset ID of the Asset to send - # @return [FundOperation] The new pending FundOperation object - # @raise [Coinbase::ApiError] If the FundOperation fails - def create(wallet_id:, address_id:, amount:, asset_id:, network:) + # @param quote [Coinbase::FundQuote, String] The optional FundQuote to use for the Fund Operation + # @return [FundOperation] The new pending Fund Operation object + # @raise [Coinbase::ApiError] If the Fund Operation fails + def create(wallet_id:, address_id:, amount:, asset_id:, network:, quote: nil) network = Coinbase::Network.from_id(network) asset = network.get_asset(asset_id) @@ -43,7 +46,8 @@ def create(wallet_id:, address_id:, amount:, asset_id:, network:) { amount: asset.to_atomic_amount(amount).to_i.to_s, asset_id: asset.primary_denomination.to_s, - } + fund_quote_id: quote_id(quote) + }.compact ) end @@ -76,6 +80,14 @@ def fetch_page(wallet_id, address_id, page) page: page ) end + + def quote_id(quote) + return nil if quote.nil? + return quote.id if quote.is_a?(FundQuote) + return quote if quote.is_a?(String) + + raise ArgumentError, 'quote must be a FundQuote object or ID' + end end # Returns a new Fund Operation object. Do not use this method directly. Instead, use diff --git a/lib/coinbase/fund_quote.rb b/lib/coinbase/fund_quote.rb index b7d4926d..05fa6f6f 100644 --- a/lib/coinbase/fund_quote.rb +++ b/lib/coinbase/fund_quote.rb @@ -57,6 +57,20 @@ def id @model.fund_quote_id end + # Executes a fund operation using the quote. + # @return [Coinbase::FundOperation] The FundOperation object + # @raise [Coinbase::ApiError] If the FundOperation fails + def execute! + FundOperation.create( + wallet_id: wallet_id, + address_id: address_id, + amount: amount.amount, + asset_id: asset.asset_id, + network: network.id, + quote: self + ) + end + # Returns the Network the fund quote was created on. # @return [Coinbase::Network] The Network def network @@ -125,5 +139,11 @@ def to_s def inspect to_s end + + private + + def fund_api + @fund_api ||= Coinbase::Client::FundApi.new(Coinbase.configuration.api_client) + end end end diff --git a/spec/unit/coinbase/fund_quote_spec.rb b/spec/unit/coinbase/fund_quote_spec.rb index 6ca6ad2a..8bce668e 100644 --- a/spec/unit/coinbase/fund_quote_spec.rb +++ b/spec/unit/coinbase/fund_quote_spec.rb @@ -120,6 +120,37 @@ end end + describe '#execute!' do + subject(:executed_operation) { fund_quote.execute! } + + let(:fund_operation) { build(:fund_operation, network_id) } + + before do + allow(Coinbase::FundOperation) + .to receive(:create) + .and_return(fund_operation) + + executed_operation + end + + it 'creates a Fund Operation' do + expect(executed_operation).to eq(fund_operation) + end + + it 'creates the fund operation with the correct details' do + expect(Coinbase::FundOperation) + .to have_received(:create) + .with( + wallet_id: model.wallet_id, + address_id: address_id, + amount: crypto_amount.amount, + asset_id: eth_asset.asset_id, + network: network_id, + quote: fund_quote + ) + end + end + describe '#id' do it 'returns the ID of the Fund Quote' do expect(fund_quote.id).to eq(model.fund_quote_id)