Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototyping canonical SolanaTBTC token with the Solang compiler #649

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cross-chain/solana/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.js
*.so
*.key
*.json
!tsconfig.json
!package.json
node_modules
yarn.lock
/test-ledger
56 changes: 56 additions & 0 deletions cross-chain/solana/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
:toc: macro

= Threshold cross-chain - Solana

This package brings Bitcoin to Solana. For more details please
see link:https://github.com/keep-network/tbtc-v2/blob/main/docs/rfc/rfc-8.adoc[RFC 8: Cross-chain Tokenized Threshold BTC]

== How it works?

```
+----------------------------+ +-----------------------------------------------------------------------+
| Ethereum | | Solana |
| | | |
| +----------------------+ | | +----------------------+ +-----------------------+ +------------+ |
| | Wormhole TokenBridge |--|---------|--| Wormhole TokenBridge |--| SolanaWormholeGateway |--| SolanaTBTC | |
| +----------------------+ | | +----------------------+ +-----------------------+ +------------+ |
| | | |
+----------------------------+ +-----------------------------------------------------------------------+
```

- `SolanaTBTC` canonical tBTC token on Solana with a minting authority
delegated to `SolanaWormholeGateway`.
- `SolanaWormholeGateway` is a smart contract wrapping and unwrapping
Wormhole-specific tBTC representation into the canonical `SolanaTBTC` token.

=== Local development

For testing and debugging purposes it is convinient to run Solana cluster locally.

link:https://docs.solana.com/cli/install-solana-cli-tools#use-solanas-install-tool[Install] Solana Tool Suite

In terminal run `solana-test-validator`. For more details see link:https://docs.solana.com/developing/test-validator[Solana Test Validator]

==== Running tests

Navigate to `/cross-chain/solana` and run `runTests.sh`. This script compiles Solidity contract(s) to produce Solana artifacts.

=== Updating Wormhole Gateway mapping

TODO: add

=== Deploy contracts

TODO: add

=== Contract upgrades

Supported out of the box. See https://docs.solana.com/cli/deploy-a-program#redeploy-a-program

TODO: add examples

=== Freezing and thawing token account

Supported with a freezing authority passed as an argument during token creation.

TODO: add examples
119 changes: 119 additions & 0 deletions cross-chain/solana/SolanaTBTC.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import './solana-library/spl_token.sol';
import 'solana';

contract SolanaTBTC {
/// @notice Indicates if the given address is a minter. Only minters can
/// mint the token.
mapping(address => bool) isMinter;

/// @notice List of all minters.
address[] minters;

/// @notice owner
address authority;

/// @notice mint account
/// @dev Stores information about the token itself. E.g. current supply and
/// its authorities
address mint;

event MinterAdded(address minter);
event MinterRemoved(address minter);

modifier needs_authority() {
for (uint64 i = 0; i < tx.accounts.length; i++) {
AccountInfo ai = tx.accounts[i];
if (ai.key == authority && ai.is_signer) {
_;
return;
}
}

print("Not signed by authority");
revert("Not signed by authority");
}

modifier minter_only() {
for (uint64 i = 0; i < tx.accounts.length; i++) {
AccountInfo ai = tx.accounts[i];
print("checking if a minter");
if (isMinter[ai.key] && ai.is_signer) {
_;
return;
}
}

print("Not a minter");
revert("Not a minter");
}

constructor(address initial_authority) {
authority = initial_authority;
}

/// @notice Adds the address to the minters list.
/// @dev Requirements:
/// - The caller must have authority.
/// - `minter` must not be a minter address already.
/// @param minter The address to be added as a minter. This address can mint
/// SolanaTBTC token.
function add_minter(address minter) needs_authority public {
print("adding a minter...");
require(!isMinter[minter], "This address is already a minter");
isMinter[minter] = true;
minters.push(minter);
emit MinterAdded(minter);
print("added a minter...");
}

/// @notice Removes the address from the minters list.
/// @dev Requirements:
/// - The caller must have authority.
/// - `minter` must be a minter address.
/// @param minter The address to be removed from the minters list.
function remove_minter(address minter) public needs_authority {
require(isMinter[minter], "This address is not a minter");
delete isMinter[minter];

// We do not expect too many minters so a simple loop is safe.
for (uint256 i = 0; i < minters.length; i++) {
if (minters[i] == minter) {
minters[i] = minters[minters.length - 1];
minters.pop();
break;
}
}

emit MinterRemoved(minter);
}

function set_mint(address _mint) needs_authority public {
mint = _mint;
}

function mint_to(address account, address _authority, uint64 amount) minter_only public {
print("yo, I'm in mint_to");
SplToken.mint_to(mint, account, _authority, amount);
}

function total_supply() public view returns (uint64) {
return SplToken.total_supply(mint);
}

function get_balance(address account) public view returns (uint64) {
return SplToken.get_balance(account);
}

function transfer(address from, address to, address owner, uint64 amount) public {
SplToken.transfer(from, to, owner, amount);
}

function burn(address account, address owner, uint64 amount) public {
SplToken.burn(account, mint, owner, amount);
}

/// @notice Allows to fetch a list of all minters.
function get_minters() public view returns (address[] memory) {
return minters;
}
}
Loading