Skip to content

Commit

Permalink
Merge branch 'feat-token-extensions' into refacto-repo-structure
Browse files Browse the repository at this point in the history
  • Loading branch information
ametel01 committed Sep 14, 2024
2 parents 02be20d + c89b758 commit e0e9e72
Show file tree
Hide file tree
Showing 31 changed files with 1,771 additions and 153 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/label-prs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Label PRs for feat-token-extensions

on:
pull_request:
branches:
- feat-token-extensions

jobs:
tag-and-label:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Add label to PR
uses: actions/github-script@v7
with:
script: |
github.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: ['feat-token-extensions']
})
- name: Add Tag to PR Title
run: |
gh pr edit ${{ github.event.pull_request.number }} --add-label "feat-token-extensions"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scripts/node_modules
scripts/.env
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,23 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!


## 🪛 Deployment

This section details the steps to deploy Hyperlane contracts on Starknet. Note that the deployment script will set a basic configuration for all the required contracts. Further configuration process might be required based on the use case. Constructors parameters can be specified in the `contract_config.json`.
Firstly, set the following environment variables, important for the deployment process:
```bash
STARKNET_RPC_URL=
ACCOUNT_ADDRESS=
BENEFICIARY_ADDRESS=
NETWORK=
PRIVATE_KEY=
```
The beneficiary address is the account that will be used to recover funds from the protocol fee.
Once set, the contracts can be deployed using this command( assuming `ts-node` is installed):

```bash
ts-node deploy.ts
```
14 changes: 7 additions & 7 deletions cairo/Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = 1
[[package]]
name = "alexandria_bytes"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#b5c8356ce7d46a3665e08a8016d5abc02d9b0fe2"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
dependencies = [
"alexandria_data_structures",
"alexandria_math",
Expand All @@ -13,15 +13,15 @@ dependencies = [
[[package]]
name = "alexandria_data_structures"
version = "0.2.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#b5c8356ce7d46a3665e08a8016d5abc02d9b0fe2"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
dependencies = [
"alexandria_encoding",
]

[[package]]
name = "alexandria_encoding"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#b5c8356ce7d46a3665e08a8016d5abc02d9b0fe2"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
dependencies = [
"alexandria_bytes",
"alexandria_math",
Expand All @@ -31,15 +31,15 @@ dependencies = [
[[package]]
name = "alexandria_math"
version = "0.2.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#b5c8356ce7d46a3665e08a8016d5abc02d9b0fe2"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
dependencies = [
"alexandria_data_structures",
]

[[package]]
name = "alexandria_numeric"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#b5c8356ce7d46a3665e08a8016d5abc02d9b0fe2"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
dependencies = [
"alexandria_math",
"alexandria_searching",
Expand All @@ -48,15 +48,15 @@ dependencies = [
[[package]]
name = "alexandria_searching"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#b5c8356ce7d46a3665e08a8016d5abc02d9b0fe2"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
dependencies = [
"alexandria_data_structures",
]

[[package]]
name = "alexandria_storage"
version = "0.3.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#bcdca70afdf59c9976148e95cebad5cf63d75a7f"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70#bcdca70afdf59c9976148e95cebad5cf63d75a7f"

[[package]]
name = "hyperlane_starknet"
Expand Down
4 changes: 2 additions & 2 deletions cairo/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ cairo-version = "2.6.3"

[dependencies]
starknet = "2.6.3"
alexandria_bytes = { git = "https://github.com/keep-starknet-strange/alexandria.git" }
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" }
alexandria_bytes = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "bcdca70" }
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "bcdca70" }
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.14.0" }


Expand Down
2 changes: 1 addition & 1 deletion cairo/src/contracts/client/router_component.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub mod RouterComponent {
}

#[embeddable_as(RouterImpl)]
impl Router<
pub impl Router<
TContractState,
+HasComponent<TContractState>,
+MailboxclientComponent::HasComponent<TContractState>,
Expand Down
215 changes: 215 additions & 0 deletions cairo/src/contracts/mocks/mock_hyp_erc721_uri_storage.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
#[starknet::interface]
trait IMockHypERC721URIStorage<TContractState> {
fn set_token_uri(ref self: TContractState, token_id: u256, uri: ByteArray);
}

#[starknet::contract]
pub mod MockHypERC721URIStorage {
use alexandria_bytes::{Bytes, BytesTrait};
use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent;
use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent;
use hyperlane_starknet::contracts::client::router_component::RouterComponent;
use hyperlane_starknet::contracts::token::components::erc721_uri_storage::ERC721URIStorageComponent;
use hyperlane_starknet::contracts::token::components::hyp_erc721_component::{
HypErc721Component
};
use hyperlane_starknet::contracts::token::components::token_router::{
TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait,
TokenRouterComponent::MessageRecipientInternalHookImpl,
TokenRouterTransferRemoteHookDefaultImpl
};
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};
use openzeppelin::upgrades::{interface::IUpgradeable, upgradeable::UpgradeableComponent};
use starknet::{ContractAddress, get_caller_address};

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: MailboxclientComponent, storage: mailboxclient, event: MailboxclientEvent);
component!(path: RouterComponent, storage: router, event: RouterEvent);
component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent);
component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent);
component!(path: HypErc721Component, storage: hyp_erc721, event: HypErc721Event);
component!(path: ERC721Component, storage: erc721, event: ERC721Event);
component!(path: SRC5Component, storage: src5, event: SRC5Event);
component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
component!(
path: ERC721URIStorageComponent, storage: erc721_uri_storage, event: ERC721UriStorageEvent
);

// Ownable
#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

// MailboxClient
#[abi(embed_v0)]
impl MailboxClientImpl =
MailboxclientComponent::MailboxClientImpl<ContractState>;
impl MailboxClientInternalImpl =
MailboxclientComponent::MailboxClientInternalImpl<ContractState>;

//Router
#[abi(embed_v0)]
impl RouterImpl = RouterComponent::RouterImpl<ContractState>;
impl RouterInternalImpl = RouterComponent::RouterComponentInternalImpl<ContractState>;

// GasRouter
#[abi(embed_v0)]
impl GasRouterImpl = GasRouterComponent::GasRouterImpl<ContractState>;

// TokenRouter
#[abi(embed_v0)]
impl TokenRouterImpl = TokenRouterComponent::TokenRouterImpl<ContractState>;
impl TokenRouterInternalImpl = TokenRouterComponent::TokenRouterInternalImpl<ContractState>;

//HypERC721
impl HypErc721InternalImpl = HypErc721Component::HypErc721InternalImpl<ContractState>;

//ERC721
#[abi(embed_v0)]
impl ERC721URIStorageImpl =
ERC721URIStorageComponent::ERC721URIStorageImpl<ContractState>;
#[abi(embed_v0)]
impl ERC721Impl = ERC721Component::ERC721Impl<ContractState>;
#[abi(embed_v0)]
impl ERC721CamelOnlyImpl = ERC721Component::ERC721CamelOnlyImpl<ContractState>;
impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>;
impl ERC721URIStorageInternalImpl =
ERC721URIStorageComponent::ERC721URIStorageInternalImpl<ContractState>;

//upgradeable
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
mailboxclient: MailboxclientComponent::Storage,
#[substorage(v0)]
router: RouterComponent::Storage,
#[substorage(v0)]
gas_router: GasRouterComponent::Storage,
#[substorage(v0)]
token_router: TokenRouterComponent::Storage,
#[substorage(v0)]
hyp_erc721: HypErc721Component::Storage,
#[substorage(v0)]
erc721: ERC721Component::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
#[substorage(v0)]
upgradeable: UpgradeableComponent::Storage,
#[substorage(v0)]
erc721_uri_storage: ERC721URIStorageComponent::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: OwnableComponent::Event,
#[flat]
MailboxclientEvent: MailboxclientComponent::Event,
#[flat]
RouterEvent: RouterComponent::Event,
#[flat]
GasRouterEvent: GasRouterComponent::Event,
#[flat]
TokenRouterEvent: TokenRouterComponent::Event,
#[flat]
ERC721Event: ERC721Component::Event,
#[flat]
SRC5Event: SRC5Component::Event,
#[flat]
HypErc721Event: HypErc721Component::Event,
#[flat]
UpgradeableEvent: UpgradeableComponent::Event,
#[flat]
ERC721UriStorageEvent: ERC721URIStorageComponent::Event,
}

#[constructor]
fn constructor(
ref self: ContractState,
mailbox: ContractAddress,
_mint_amount: u256,
_name: ByteArray,
_symbol: ByteArray,
_hook: ContractAddress,
_interchainSecurityModule: ContractAddress,
owner: ContractAddress,
) {
self.ownable.initializer(owner);
self
.mailboxclient
.initialize(mailbox, Option::Some(_hook), Option::Some(_interchainSecurityModule));
self.hyp_erc721.initialize(_mint_amount, _name, _symbol);
}

#[abi(embed_v0)]
impl IMockHypERC721URIStorageImpl of super::IMockHypERC721URIStorage<ContractState> {
fn set_token_uri(ref self: ContractState, token_id: u256, uri: ByteArray) {
self.erc721_uri_storage._set_token_uri(token_id, uri);
}
}

#[abi(embed_v0)]
impl HypErc721Upgradeable of IUpgradeable<ContractState> {
fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) {
self.ownable.assert_only_owner();
self.upgradeable.upgrade(new_class_hash);
}
}

impl TokenRouterHooksImpl of TokenRouterHooksTrait<ContractState> {
fn transfer_from_sender_hook(
ref self: TokenRouterComponent::ComponentState<ContractState>, amount_or_id: u256
) -> Bytes {
let contract_state = TokenRouterComponent::HasComponent::get_contract(@self);
let token_owner = contract_state.erc721.owner_of(amount_or_id);
assert!(token_owner == get_caller_address(), "Caller is not owner of token");

let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self);
contract_state.erc721.burn(amount_or_id);

BytesTrait::new_empty()
}

fn transfer_to_hook(
ref self: TokenRouterComponent::ComponentState<ContractState>,
recipient: u256,
amount_or_id: u256,
metadata: Bytes
) {
let recipient_felt: felt252 = recipient.try_into().expect('u256 to felt failed');
let recipient: ContractAddress = recipient_felt.try_into().unwrap();

let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self);

let metadata_byteArray = bytes_to_byte_array(metadata);
contract_state.erc721_uri_storage._set_token_uri(amount_or_id, metadata_byteArray);
contract_state.erc721.mint(recipient, amount_or_id);
}
}

// free function
fn bytes_to_byte_array(self: Bytes) -> ByteArray {
let mut res: ByteArray = Default::default();
let mut offset = 0;
while offset < self
.size() {
if offset + 31 <= self.size() {
let (new_offset, value) = self.read_bytes31(offset);
res.append_word(value.into(), 31);
offset = new_offset;
} else {
let (new_offset, value) = self.read_u8(offset);
res.append_byte(value);
offset = new_offset;
}
};
res
}
}
6 changes: 6 additions & 0 deletions cairo/src/contracts/mocks/test_erc721.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub trait ITestERC721<TState> {
fn is_approved_for_all(
self: @TState, owner: ContractAddress, operator: ContractAddress
) -> bool;
fn token_uri(self: @TState, token_id: u256) -> ByteArray;
}

#[starknet::contract]
Expand All @@ -33,6 +34,7 @@ mod TestERC721 {
component!(path: SRC5Component, storage: src5, event: SRC5Event);

impl ERC721Impl = ERC721Component::ERC721Impl<ContractState>;
impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl<ContractState>;
impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>;

#[storage]
Expand Down Expand Up @@ -107,5 +109,9 @@ mod TestERC721 {
) -> bool {
self.erc721.is_approved_for_all(owner, operator)
}

fn token_uri(self: @ContractState, token_id: u256) -> ByteArray {
self.erc721.token_uri(token_id)
}
}
}
Loading

0 comments on commit e0e9e72

Please sign in to comment.