Skip to content

Commit

Permalink
Release: V0.1.0 (#159)
Browse files Browse the repository at this point in the history
### What changed? Why?

## [0.1.0] - 2024-08-22

- Add support for listing historical balances for a wallet, defaulting
to the
historical_balances function for wallet: listing historical balances for
default address of the wallet.
- Expose all networks as constants, e.g.
`Coinbase::Network::ETHEREUM_MAINNET`
- Add support for managing Webhooks.
- Add support for listing smart contract events.
- Add support for Dedicated ETH Staking for wallet addresses

### Breaking Changes
- All method signatures that took a `network_id` now take a `network`
that can be either a network constant (e.g.
`Coinbase::Network::BASE_MAINNET`) or a network ID (e.g.
`:base_mainnet`)

### Added

- Add to_address_id method to Transaction class
- Remove "pending" status from staking operation status

#148
#140
#154
#149
#156
#136
#150
#153
#138
#158
#155
#161
#157

#### Qualified Impact
<!-- Please evaluate what components could be affected and what the
impact would be if there was an
error. How would this error be resolved, e.g. rollback a deploy, push a
new fix, disable a feature
flag, etc... -->

---------

Co-authored-by: Chaoya Ji <chaoya.ji@coinbase.com>
Co-authored-by: xinyu-li-cb <142266583+xinyu-li-cb@users.noreply.github.com>
Co-authored-by: Rohit Durvasula <rohit.durvasula@coinbase.com>
Co-authored-by: Rohit Durvasula <88731568+drohit-cb@users.noreply.github.com>
Co-authored-by: CJ_Andy <109100734+chaoyaji-cb@users.noreply.github.com>
Co-authored-by: Shreif Abdallah <shreif.abdallah@coinbase.com>
Co-authored-by: shreifabdallah <129977880+shreifabdallah@users.noreply.github.com>
Co-authored-by: jinghanx <jinghanx@gmail.com>
  • Loading branch information
9 people authored Aug 22, 2024
1 parent a1f9ee5 commit 408e36a
Show file tree
Hide file tree
Showing 152 changed files with 2,793 additions and 2,818 deletions.
3 changes: 0 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ Metrics/BlockLength:
# Classes can be long.
Metrics/ClassLength:
Max: 300
Exclude:
- "lib/coinbase/wallet.rb" # TODO: Refactor wallet to remove delegatable functions.

# Cyclomatic complexity is not a good measure of code quality.
Metrics/CyclomaticComplexity:
Max: 50
Expand Down
26 changes: 11 additions & 15 deletions CAPABILITIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,20 @@ those capabilities for the Ruby SDK:
## Developer Wallets

| Concept | Base-Sepolia | Base-Mainnet | Ethereum-Holesky | Ethereum-Mainnet |
| ------------- | :----------: | :----------: | :--------------: | :--------------: |
| Addresses |||||
| Send |||||
| Trade |||||
| Faucet |||||
| Server-Signer |||||
| Stake [^1] |||||

[^1]: Currently only available for Shared ETH Staking.
|---------------|:------------:|:------------:|:----------------:|:----------------:|
| Addresses |||||
| Send |||||
| Trade |||||
| Faucet |||||
| Server-Signer |||||
| Stake |||||

## End-User Wallets

| Concept | Base-Sepolia | Base-Mainnet | Ethereum-Holesky | Ethereum-Mainnet |
| ------------------ | :----------: | :----------: | :--------------: | :--------------: |
| External Addresses |||||
| Stake [^2] |||||

[^2]: Dedicated ETH Staking is currently only available on Testnet (Ethereum-Holesky).
|--------------------|:------------:|:------------:|:----------------:|:----------------:|
| External Addresses |||||
| Stake |||||

## Testnet vs. Mainnet

Expand All @@ -32,4 +28,4 @@ The Coinbase SDK supports both testnets and mainnets.
- Testnets are for building and testing applications. Funds are not real, and you can get test currencies from a faucet.
- Mainnet is where the funds, contracts and applications are real.

Wallets, assets, etc, cannot be moved from testnet to mainnet (or vice versa).
Wallets, assets, etc. cannot be moved from testnet to mainnet (or vice versa).
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## [0.1.0] - 2024-08-22

- Expose all networks as constants, e.g. `Coinbase::Network::ETHEREUM_MAINNET`
- Add support for managing Webhooks.
- Add support for listing smart contract events.
- Add support for Dedicated ETH Staking for wallet addresses

### Breaking Changes
- All method signatures that took a `network_id` now take a `network` that can be either a network constant (e.g. `Coinbase::Network::BASE_MAINNET`) or a network ID (e.g. `:base_mainnet`)

### Added

- Add to_address_id method to Transaction class
- Remove "pending" status from staking operation status

## [0.0.16] - 2024-08-14

Expand Down
64 changes: 25 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,30 +155,26 @@ Coinbase.configure_from_json('~/Downloads/cdp_api_key.json')
puts "Coinbase SDK has been successfully configured from JSON file."
```

This will allow you to [authenticate](./authentication.md) with the Platform APIs and get access to the
[`default_user`](./users.md):

```ruby
u = Coinbase.default_user
```
This will allow you to [authenticate](./authentication.md) with the Platform APIs.

If you are using a CDP Server-Signer to manage your private keys, enable it with

```ruby
Coinbase.configuration.use_server_signer=true
```
Now, create a wallet from the User. Wallets are created with a single default address.

Now create a wallet. Wallets are created with a single default address.

```ruby
# Create a wallet with one address by default.
w1 = u.create_wallet
wallet1 = Coinbase::Wallet.create
```

Wallets come with a single default address, accessible via `default_address`:

```ruby
# A wallet has a default address.
a = w1.default_address
address = wallet1.default_address
```

## Funding a Wallet
Expand All @@ -188,7 +184,7 @@ testnet ETH. You are allowed one faucet claim per 24-hour window.

```ruby
# Fund the wallet with a faucet transaction.
faucet_tx = w1.faucet
faucet_tx = wallet1.faucet

puts "Faucet transaction successfully completed: #{faucet_tx}"
```
Expand All @@ -202,27 +198,27 @@ The code below creates another wallet, and uses the `transfer` function to send
the second:

```ruby
# Create a new wallet w2 to transfer funds to.
w2 = u.create_wallet
# Create a new wallet wallet2 to transfer funds to.
wallet2 = Coinbase::Wallet.create

puts "Wallet successfully created: #{w2}"
puts "Wallet successfully created: #{wallet2}"

t = w1.transfer(0.00001, :eth, w2).wait!
transfer = wallet1.transfer(0.00001, :eth, wallet2).wait!

puts "Transfer successfully completed: #{t}"
puts "Transfer successfully completed: #{transfer}"
```

### Gasless USDC Transfers

To transfer USDC without needing to hold ETH for gas, you can use the `transfer` method with the `gasless` option set to `true`.

```ruby
# Create a new wallet w3 to transfer funds to.
w3 = u.create_wallet
# Create a new wallet wallet3 to transfer funds to.
wallet3 = Coinbase::Wallet.create

puts "Wallet successfully created: #{w3}"
puts "Wallet successfully created: #{wallet3}"

t = w1.transfer(0.00001, :usdc, w3, gasless: true).wait!
transfer = wallet1.transfer(0.00001, :usdc, wallet3, gasless: true).wait!
```

## Listing Transfers
Expand All @@ -245,8 +241,7 @@ address.transfers.to_a
See [Trades](https://docs.cdp.coinbase.com/wallets/docs/trades) for more information.

```ruby

wallet = Coinbase::Wallet.create(network_id: "base-mainnet")
wallet = Coinbase::Wallet.create(network: Coinbase::Network::BASE_MAINNET)

puts "Wallet successfully created: #{wallet}"
puts "Send `base-mainnet` ETH to wallets default address: #{wallet.default_address.id}"
Expand Down Expand Up @@ -277,7 +272,7 @@ The SDK creates wallets with developer managed keys, which means you are respons

```ruby
# Export the data required to re-instantiate the wallet.
data = w1.export
data = wallet1.export
```

In order to persist the data for a wallet, you will need to implement a `store` method to store the exported data in a secure location. If you do not store the wallet in a secure location, you will lose access to the wallet, as well as the funds on it.
Expand All @@ -299,9 +294,9 @@ convenience function purely for testing purposes, and should not be considered a
file_path = 'my_seed.json'

# Set encrypt: true to encrypt the wallet seed with your CDP API key.
w1.save_seed!(file_path, encrypt: true)
wallet1.save_seed!(file_path, encrypt: true)

puts "Seed for wallet #{w1.id} successfully saved to #{file_path}."
puts "Seed for wallet #{wallet1.id} successfully saved to #{file_path}."
```

## Re-instantiating a Wallet
Expand All @@ -311,22 +306,22 @@ To re-instantiate a wallet, fetch your export data from your secure storage, and
```ruby
# You should implement the "fetch" method to retrieve the securely persisted data object,
# keyed by the wallet ID.
fetched_data = fetch(w1.id)
fetched_data = fetch(wallet1.id)

# w3 will be equivalent to w1.
w3 = u.import_wallet(fetched_data)
# wallet3 will be equivalent to wallet1.
wallet3 = Coinbase::Wallet.import(fetched_data)
```

If you used the `save_seed!` function to persist a wallet's seed to a local file, then you can first fetch
the unhydrated wallet from the server, and then use the `load_seed` method to re-instantiate the wallet.

```ruby
# Get the unhydrated wallet from the server.
w4 = u.wallet(w1.id)
wallet4 = Coinbase::Wallet.fetch(wallet1.id)

# You can now load the seed into the wallet from the local file.
# w4 will be equivalent to w1.
w4.load_seed(file_path)
# wallet4 will be equivalent to wallet1.
wallet4.load_seed(file_path)
```

## External Addresses
Expand Down Expand Up @@ -367,15 +362,6 @@ This SDK transitively depends on [rbsecp256k1](https://github.com/etscrivner/rbs
[these instructions](https://github.com/etscrivner/rbsecp256k1?tab=readme-ov-file#requirements) to
ensure you have the necessary dependencies installed.

The SDK assumes the existence of a `BASE_SEPOLIA_RPC_URL` environment variable defined in your .env file.
By default, this is the publicly available endpoint, which is rate-limited.
To provision your own endpoint, go to the [CDP Portal](https://portal.cloud.coinbase.com/products/base). Then
copy and paste your Base Sepolia RPC URL in the .env file:

```
BASE_SEPOLIA_RPC_URL=YOUR-URL
```

### Linting

To autocorrect all lint errors, run:
Expand Down
4 changes: 3 additions & 1 deletion coinbase.gemspec
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# frozen_string_literal: true

require_relative 'lib/coinbase/version'

Gem::Specification.new do |spec|
spec.name = 'coinbase-sdk'
spec.version = '0.0.16'
spec.version = Coinbase::VERSION
spec.authors = ['Yuga Cohler']
spec.files = Dir['lib/**/*.rb']
spec.summary = 'Coinbase Ruby SDK'
Expand Down
69 changes: 50 additions & 19 deletions lib/coinbase.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
require_relative 'coinbase/address/external_address'
require_relative 'coinbase/asset'
require_relative 'coinbase/authenticator'
require_relative 'coinbase/correlation'
require_relative 'coinbase/balance'
require_relative 'coinbase/balance_map'
require_relative 'coinbase/historical_balance'
require_relative 'coinbase/client'
require_relative 'coinbase/constants'
require_relative 'coinbase/contract_event'
require_relative 'coinbase/destination'
require_relative 'coinbase/errors'
require_relative 'coinbase/faucet_transaction'
Expand All @@ -19,14 +21,16 @@
require_relative 'coinbase/trade'
require_relative 'coinbase/transfer'
require_relative 'coinbase/transaction'
require_relative 'coinbase/user'
require_relative 'coinbase/wallet'
require_relative 'coinbase/server_signer'
require_relative 'coinbase/smart_contract'
require_relative 'coinbase/sponsored_send'
require_relative 'coinbase/staking_balance'
require_relative 'coinbase/staking_operation'
require_relative 'coinbase/staking_reward'
require_relative 'coinbase/validator'
require_relative 'coinbase/version'
require_relative 'coinbase/webhook'
require 'json'

# The Coinbase SDK.
Expand Down Expand Up @@ -73,6 +77,17 @@ def initialize
@debug_api = false
@use_server_signer = false
@max_network_tries = 3
@default_network = Coinbase::Network::BASE_SEPOLIA
end

attr_reader :default_network

def default_network=(network)
unless network.is_a?(Coinbase::Network)
raise InvalidConfiguration, 'Default network must use a network constant, e.g. Coinbase::Network::BASE_MAINNET'
end

@default_network = network
end

# Sets configuration values based on the provided CDP API Key JSON file.
Expand All @@ -97,32 +112,21 @@ def api_client
end
end

# Returns the default user.
# @return [Coinbase::User] the default user
def self.default_user
@default_user ||= load_default_user
end

# Converts a string to a symbol, replacing hyphens with underscores.
# @param string [String] the string to convert
# @return [Symbol] the converted symbol
def self.to_sym(value)
value.to_s.gsub('-', '_').to_sym
end

# Converts a network symbol to a string, replacing underscores with hyphens.
# @param network_sym [Symbol] the network symbol to convert
# @return [String] the converted string
def self.normalize_network(network_sym)
network_sym.to_s.gsub('_', '-')
end
# Converts a Network object or network symbol with the string representation of the network ID,
# replacing underscores with hyphens.
# @param network_sym [Coinbase::Network, Symbol] The network or network symbol to convert
# @return [String] The string representation of the network ID
def self.normalize_network(network)
network_sym = network.is_a?(Coinbase::Network) ? network.id : network

# Loads the default user.
# @return [Coinbase::User] the default user
def self.load_default_user
users_api = Coinbase::Client::UsersApi.new(configuration.api_client)
user_model = users_api.get_current_user
Coinbase::User.new(user_model)
network_sym.to_s.gsub('_', '-')
end

# Wraps a call to the Platform API to ensure that the error is caught and
Expand Down Expand Up @@ -150,9 +154,36 @@ def self.use_server_signer?
Coinbase.configuration.use_server_signer
end

# Returns the default network.
# @return [Coinbase::Network] the default network
def self.default_network
Coinbase.configuration.default_network
end

# Returns whether the SDK is configured.
# @return [bool] whether the SDK is configured
def self.configured?
!Coinbase.configuration.api_key_name.nil? && !Coinbase.configuration.api_key_private_key.nil?
end
end

# Initialize the Network constants.
Coinbase::Network.const_set(
'ALL',
Coinbase::Client::NetworkIdentifier.all_vars.each_with_object([]) do |id, list|
next if id == Coinbase::Client::NetworkIdentifier::UNKNOWN_DEFAULT_OPEN_API

network = Coinbase::Network.new(id)

Coinbase::Network.const_set(id.upcase.gsub('-', '_'), network)

list << network
end
)

Coinbase::Network.const_set(
'NETWORK_MAP',
Coinbase::Network::ALL.each_with_object({}) do |network, map|
map[network.id] = network
end
)
Loading

0 comments on commit 408e36a

Please sign in to comment.