Skip to content

Commit

Permalink
Merge pull request #218 from hirosystems/feat/fungible-token-rpc-endp…
Browse files Browse the repository at this point in the history
…oint

feat(rpc): Add RPC endpoint for Fungible Tokens
  • Loading branch information
obycode authored Feb 23, 2023
2 parents 422d2d9 + 63827c5 commit 048c71e
Show file tree
Hide file tree
Showing 8 changed files with 899 additions and 44 deletions.
10 changes: 6 additions & 4 deletions core-contracts/contracts/helper/simple-ft-l2.clar
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

;; get the token balance of owner
(define-read-only (get-balance (owner principal))
(begin
(ok (ft-get-balance ft-token owner))))
(ok (ft-get-balance ft-token owner)))

;; returns the total number of tokens
(define-read-only (get-total-supply)
Expand Down Expand Up @@ -38,8 +37,11 @@
(ok none)
)

(define-read-only (get-token-balance (user principal))
(ft-get-balance ft-token user)
(define-public (gift-tokens (amount uint) (recipient principal))
(begin
(asserts! (is-eq tx-sender recipient) ERR_NOT_AUTHORIZED)
(ft-mint? ft-token amount recipient)
)
)

(impl-trait 'ST000000000000000000002AMW42H.subnet.subnet-asset)
Expand Down
7 changes: 3 additions & 4 deletions core-contracts/contracts/helper/simple-ft.clar
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

;; get the token balance of owner
(define-read-only (get-balance (owner principal))
(begin
(ok (ft-get-balance ft-token owner))))
(ok (ft-get-balance ft-token owner)))

;; returns the total number of tokens
(define-read-only (get-total-supply)
Expand Down Expand Up @@ -49,9 +48,9 @@
(define-read-only (get-token-uri)
(ok none))

(define-public (gift-tokens (recipient principal))
(define-public (gift-tokens (amount uint) (recipient principal))
(begin
(asserts! (is-eq tx-sender recipient) ERR_NOT_AUTHORIZED)
(ft-mint? ft-token u1 recipient)
(ft-mint? ft-token amount recipient)
)
)
10 changes: 5 additions & 5 deletions core-contracts/tests/subnets/subnet_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ Clarinet.test({
Tx.contractCall(
"simple-ft",
"gift-tokens",
[types.principal(charlie.address)],
[types.uint(1), types.principal(charlie.address)],
charlie.address
),
]);
Expand All @@ -614,7 +614,7 @@ Clarinet.test({
Tx.contractCall(
"simple-ft",
"gift-tokens",
[types.principal(charlie.address)],
[types.uint(1), types.principal(charlie.address)],
charlie.address
),
]);
Expand Down Expand Up @@ -844,7 +844,7 @@ Clarinet.test({
Tx.contractCall(
"simple-ft",
"gift-tokens",
[types.principal(charlie.address)],
[types.uint(1), types.principal(charlie.address)],
charlie.address
),
]);
Expand Down Expand Up @@ -1055,7 +1055,7 @@ Clarinet.test({
Tx.contractCall(
"simple-ft",
"gift-tokens",
[types.principal(charlie.address)],
[types.uint(1), types.principal(charlie.address)],
charlie.address
),
]);
Expand All @@ -1065,7 +1065,7 @@ Clarinet.test({
Tx.contractCall(
"simple-ft",
"gift-tokens",
[types.principal(charlie.address)],
[types.uint(1), types.principal(charlie.address)],
charlie.address
),
]);
Expand Down
80 changes: 79 additions & 1 deletion src/net/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ lazy_static! {
.unwrap();
static ref PATH_POST_BLOCK_PROPOSAL: Regex = Regex::new(&format!("^{}$", PATH_STR_POST_BLOCK_PROPOSAL))
.unwrap();
static ref PATH_GET_FT_WITHDRAWAL: Regex = Regex::new(&format!(
"^/v2/withdrawal/ft/(?P<block_height>[0-9]+)/(?P<sender>{})/(?P<withdrawal_id>[0-9]+)/(?P<contract_address>{})/(?P<contract_name>{})/(?P<amount>[0-9]+)$",
*PRINCIPAL_DATA_REGEX_STRING, *STANDARD_PRINCIPAL_REGEX_STRING, *CONTRACT_NAME_REGEX_STRING
))
.unwrap();
static ref PATH_GET_NFT_WITHDRAWAL: Regex = Regex::new(&format!(
"^/v2/withdrawal/nft/(?P<block_height>[0-9]+)/(?P<sender>{})/(?P<withdrawal_id>[0-9]+)/(?P<contract_address>{})/(?P<contract_name>{})/(?P<id>[0-9]+)$",
*PRINCIPAL_DATA_REGEX_STRING, *STANDARD_PRINCIPAL_REGEX_STRING, *CONTRACT_NAME_REGEX_STRING
Expand Down Expand Up @@ -1629,6 +1634,11 @@ impl HttpRequestType {
&PATH_POST_BLOCK_PROPOSAL,
&HttpRequestType::parse_block_proposal,
),
(
"GET",
&PATH_GET_FT_WITHDRAWAL,
&HttpRequestType::parse_get_ft_withdrawal,
),
(
"GET",
&PATH_GET_NFT_WITHDRAWAL,
Expand Down Expand Up @@ -1859,6 +1869,50 @@ impl HttpRequestType {
})
}

fn parse_get_ft_withdrawal<R: Read>(
_protocol: &mut StacksHttp,
preamble: &HttpRequestPreamble,
captures: &Captures,
_query: Option<&str>,
_fd: &mut R,
) -> Result<HttpRequestType, net_error> {
if preamble.get_content_length() != 0 {
return Err(net_error::DeserializeError(
"Invalid Http request: expected 0-length body for GetFTWithdrawal".to_string(),
));
}

let sender = PrincipalData::parse(&captures["sender"]).map_err(|_e| {
net_error::DeserializeError("Failed to parse account principal".into())
})?;

let withdraw_block_height = u64::from_str(&captures["block_height"])
.map_err(|_e| net_error::DeserializeError("Failed to parse block height".into()))?;

let withdrawal_id = u32::from_str(&captures["withdrawal_id"])
.map_err(|_e| net_error::DeserializeError("Failed to parse block height".into()))?;
let contract_addr =
StacksAddress::from_string(&captures["contract_address"]).ok_or_else(|| {
net_error::DeserializeError("Failed to parse contract address".into())
})?;
let contract_name = ContractName::try_from(captures["contract_name"].to_string())
.map_err(|_e| net_error::DeserializeError("Failed to parse contract name".into()))?;
let amount = u128::from_str(&captures["amount"])
.map_err(|_e| net_error::DeserializeError("Failed to parse amount".into()))?;

Ok(HttpRequestType::GetWithdrawalFt {
metadata: HttpRequestMetadata::from_preamble(preamble),
withdraw_block_height,
sender,
withdrawal_id,
contract_identifier: QualifiedContractIdentifier::new(
contract_addr.into(),
contract_name,
),
amount,
})
}

fn parse_get_nft_withdrawal<R: Read>(
_protocol: &mut StacksHttp,
preamble: &HttpRequestPreamble,
Expand All @@ -1868,7 +1922,7 @@ impl HttpRequestType {
) -> Result<HttpRequestType, net_error> {
if preamble.get_content_length() != 0 {
return Err(net_error::DeserializeError(
"Invalid Http request: expected 0-length body for GetAccount".to_string(),
"Invalid Http request: expected 0-length body for GetNFTWithdrawal".to_string(),
));
}

Expand Down Expand Up @@ -2818,6 +2872,7 @@ impl HttpRequestType {
HttpRequestType::ClientError(ref md, ..) => md,
HttpRequestType::GetWithdrawalStx { ref metadata, .. } => metadata,
HttpRequestType::BlockProposal(ref metadata, ..) => metadata,
HttpRequestType::GetWithdrawalFt { ref metadata, .. } => metadata,
HttpRequestType::GetWithdrawalNft { ref metadata, .. } => metadata,
}
}
Expand Down Expand Up @@ -2853,6 +2908,9 @@ impl HttpRequestType {
HttpRequestType::GetWithdrawalStx {
ref mut metadata, ..
} => metadata,
HttpRequestType::GetWithdrawalFt {
ref mut metadata, ..
} => metadata,
HttpRequestType::GetWithdrawalNft {
ref mut metadata, ..
} => metadata,
Expand Down Expand Up @@ -3035,6 +3093,22 @@ impl HttpRequestType {
withdraw_block_height, sender, withdrawal_id, amount
),
HttpRequestType::BlockProposal(..) => self.get_path().to_string(),
HttpRequestType::GetWithdrawalFt {
metadata: _,
withdraw_block_height,
sender,
withdrawal_id,
contract_identifier,
amount,
} => format!(
"/v2/withdrawal/ft/{}/{}/{}/{}/{}/{}",
withdraw_block_height,
sender,
withdrawal_id,
StacksAddress::from(contract_identifier.issuer.clone()),
contract_identifier.name.as_str(),
amount
),
HttpRequestType::GetWithdrawalNft {
metadata: _,
withdraw_block_height,
Expand Down Expand Up @@ -3090,6 +3164,9 @@ impl HttpRequestType {
"/v2/withdrawal/stx/:block-height/:sender/:withdrawal_id/:amount"
}
HttpRequestType::BlockProposal(..) => PATH_STR_POST_BLOCK_PROPOSAL,
HttpRequestType::GetWithdrawalFt { .. } => {
"/v2/withdrawal/ft/:block-height/:sender/:withdrawal_id/:contract_address/:contract_name/:amount"
}
HttpRequestType::GetWithdrawalNft { .. } => {
"/v2/withdrawal/nft/:block-height/:sender/:withdrawal_id/:contract_address/:contract_name/:id"
}
Expand Down Expand Up @@ -4624,6 +4701,7 @@ impl MessageSequence for StacksHttpMessage {
HttpRequestType::FeeRateEstimate(_, _, _) => "HTTP(FeeRateEstimate)",
HttpRequestType::GetWithdrawalStx { .. } => "HTTP(GetWithdrawalStx)",
HttpRequestType::BlockProposal(_, _) => "HTTP(BlockProposal)",
HttpRequestType::GetWithdrawalFt { .. } => "HTTP(GetWithdrawalFt)",
HttpRequestType::GetWithdrawalNft { .. } => "HTTP(GetWithdrawalNft)",
},
StacksHttpMessage::Response(ref res) => match res {
Expand Down
8 changes: 8 additions & 0 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,14 @@ pub enum HttpRequestType {
withdrawal_id: u32,
amount: u128,
},
GetWithdrawalFt {
metadata: HttpRequestMetadata,
withdraw_block_height: u64,
sender: PrincipalData,
withdrawal_id: u32,
contract_identifier: QualifiedContractIdentifier,
amount: u128,
},
GetWithdrawalNft {
metadata: HttpRequestMetadata,
withdraw_block_height: u64,
Expand Down
67 changes: 67 additions & 0 deletions src/net/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,38 @@ impl ConversationHttp {
)
}

fn handle_get_withdrawal_ft_entry<W: Write>(
http: &mut StacksHttp,
fd: &mut W,
req: &HttpRequestType,
chainstate: &mut StacksChainState,
canonical_tip: &StacksBlockId,
requested_block_height: u64,
sender: &PrincipalData,
withdrawal_id: u32,
contract_identifier: &QualifiedContractIdentifier,
id: u128,
canonical_stacks_tip_height: u64,
) -> Result<(), net_error> {
let withdrawal_key = withdrawal::make_key_for_ft_withdrawal(
sender,
withdrawal_id,
contract_identifier,
id,
requested_block_height,
);
Self::handle_get_generic_withdrawal_entry(
http,
fd,
req,
chainstate,
canonical_tip,
requested_block_height,
withdrawal_key,
canonical_stacks_tip_height,
)
}

fn handle_get_withdrawal_nft_entry<W: Write>(
http: &mut StacksHttp,
fd: &mut W,
Expand Down Expand Up @@ -2855,6 +2887,41 @@ impl ConversationHttp {
)?;
None
}

HttpRequestType::GetWithdrawalFt {
withdraw_block_height,
ref sender,
withdrawal_id,
amount,
ref contract_identifier,
..
} => {
if let Some(tip) = ConversationHttp::handle_load_stacks_chain_tip(
&mut self.connection.protocol,
&mut reply,
&req,
&TipRequest::UseLatestAnchoredTip,
sortdb,
chainstate,
network.burnchain_tip.canonical_stacks_tip_height,
)? {
ConversationHttp::handle_get_withdrawal_ft_entry(
&mut self.connection.protocol,
&mut reply,
&req,
chainstate,
&tip,
withdraw_block_height,
&sender.clone(),
withdrawal_id,
contract_identifier,
amount,
network.burnchain_tip.canonical_stacks_tip_height,
)?;
}
None
}

HttpRequestType::GetWithdrawalNft {
withdraw_block_height,
ref sender,
Expand Down
Loading

0 comments on commit 048c71e

Please sign in to comment.