Skip to content

Commit

Permalink
Fix registration of proxied ERC20 deposits (#1289)
Browse files Browse the repository at this point in the history
  • Loading branch information
ofux committed Nov 15, 2024
1 parent dc1b4bc commit bc68049
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ public Map<SponsorAccount, PositiveAmount> transferredAmountPerOrigin(RewardId i
}

@Override
@Transactional
public Deposit previewDeposit(final @NonNull UserId userId, final @NonNull SponsorId sponsorId, final @NonNull Network network,
final @NonNull String transactionReference) {
if (!permissionPort.isUserSponsorLead(userId, sponsorId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,91 @@ void should_preview_a_deposit_of_usdc_on_ethereum() {
""");
}

@Test
void should_preview_a_proxied_deposit_of_usdc_on_ethereum() {
// Given
onlyDustWallets.setEthereum("0x8371e21f595dbf98caffdcef665ebcaccb983cb1");
final var depositId = new MutableObject<String>();

// When
client.post()
.uri(getApiURI(SPONSOR_DEPOSITS.formatted(sponsor.id())))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + caller.jwt())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue("""
{
"network": "ETHEREUM",
"transactionReference": "0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd"
}
""")
.exchange()
// Then
.expectStatus()
.isOk()
.expectBody()
.jsonPath("$.id").value(depositId::setValue);

client.get()
.uri(getApiURI(DEPOSIT_BY_ID.formatted(depositId)))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + caller.jwt())
.exchange()
// Then
.expectStatus()
.isOk()
.expectBody()
.jsonPath("$.senderInformation.name").isEqualTo(sponsor.name())
.json("""
{
"amount": {
"amount": 23000.000000,
"prettyAmount": 23000.00,
"currency": {
"id": "562bbf65-8a71-4d30-ad63-520c0d68ba27",
"code": "USDC",
"name": "USD Coin",
"logoUrl": "https://s2.coinmarketcap.com/static/img/coins/64x64/3408.png",
"decimals": 6
},
"usdEquivalent": 23230.02,
"usdConversionRate": 1.010001
},
"status": "DRAFT",
"currentBalance": {
"amount": 0,
"prettyAmount": 0,
"currency": {
"id": "562bbf65-8a71-4d30-ad63-520c0d68ba27",
"code": "USDC",
"name": "USD Coin",
"logoUrl": "https://s2.coinmarketcap.com/static/img/coins/64x64/3408.png",
"decimals": 6
},
"usdEquivalent": 0.00,
"usdConversionRate": 1.010001
},
"finalBalance": {
"amount": 23000.000000,
"prettyAmount": 23000.00,
"currency": {
"id": "562bbf65-8a71-4d30-ad63-520c0d68ba27",
"code": "USDC",
"name": "USD Coin",
"logoUrl": "https://s2.coinmarketcap.com/static/img/coins/64x64/3408.png",
"decimals": 6
},
"usdEquivalent": 23230.02,
"usdConversionRate": 1.010001
},
"senderInformation": {
"accountNumber": "0x13b2639533ec7741172563b490b64cde14a34258",
"transactionReference": "0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd"
},
"billingInformation": null,
"latestBillingInformation": null
}
""");
}

@Test
void should_preview_a_deposit_of_eth_on_optimism() {
// Given
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"accessList": [],
"blockHash": "0xef8b66f05b3d7da11d46791de8888e3f62720a0b334d1cd50d3fcf7017cb27ef",
"blockNumber": "0x1430db7",
"chainId": "0x1",
"from": "0x3ab6ca1c61b37312c7ab3816087dc64237d09fe2",
"gas": "0x155b4",
"gasPrice": "0x551e0d961",
"hash": "0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd",
"input": "0x6a761202000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000cc283c1ced6f9eaf288efee2e924cd01e5578990000000000000000000000000000000000000000000000000000000055ae826000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3c6277fcc2492884d46aaa3b741ae298ae286ce795b73aef57b4a1840a3cd7293063078f0643c5cab6fc309018992960f5609fcdc5ebd1cd908706a07867448141b0000000000000000000000003ab6ca1c61b37312c7ab3816087dc64237d09fe2000000000000000000000000000000000000000000000000000000000000000001c687231683a177b9dbb0cb79c59c5570240dccdf231305ef783a13f7b4687b9b34c94d754497a3d7c9aab2f1f7c31b09986c2507220f69c4ecfe67ea5d455ead1c0000000000000000000000000000000000000000000000000000000000",
"maxFeePerGas": "0x619264a7c",
"maxPriorityFeePerGas": "0x7029fd40",
"nonce": "0x10",
"r": "0xa27c415ed8f8b52bc34b9e1235c73386711f1f06cfe835175f9d5b36b948d33",
"s": "0x4ff1b160159a77479d86121655022740a3771ee2a68a527dcf9f3063cdfbf87c",
"to": "0x13b2639533ec7741172563b490b64cde14a34258",
"transactionIndex": "0xb2",
"type": "0x2",
"v": "0x0",
"value": "0x0",
"yParity": "0x0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"jsonrpc": "2.0",
"id": {{jsonPath request.body '$.id'}},
"result": {
"blockHash": "0xef8b66f05b3d7da11d46791de8888e3f62720a0b334d1cd50d3fcf7017cb27ef",
"blockNumber": "0x1430db7",
"contractAddress": null,
"cumulativeGasUsed": "0x11c0c51",
"effectiveGasPrice": "0x551e0d961",
"from": "0x3ab6ca1c61b37312c7ab3816087dc64237d09fe2",
"gasUsed": "0x15216",
"logs": [
{
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"blockHash": "0xef8b66f05b3d7da11d46791de8888e3f62720a0b334d1cd50d3fcf7017cb27ef",
"blockNumber": "0x1430db7",
"data": "0x000000000000000000000000000000000000000000000000000000055ae82600",
"logIndex": "0x231",
"removed": false,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000013b2639533ec7741172563b490b64cde14a34258",
"0x0000000000000000000000008371e21f595dbf98caffdcef665ebcaccb983cb1"
],
"transactionHash": "0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd",
"transactionIndex": "0xb2"
},
{
"address": "0x13b2639533ec7741172563b490b64cde14a34258",
"blockHash": "0xef8b66f05b3d7da11d46791de8888e3f62720a0b334d1cd50d3fcf7017cb27ef",
"blockNumber": "0x1430db7",
"data": "0xfd0caa8036e464ffca63457a88b9bf516f415897cbbc50af8c254758141ed3310000000000000000000000000000000000000000000000000000000000000000",
"logIndex": "0x232",
"removed": false,
"topics": [
"0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e"
],
"transactionHash": "{{jsonPath request.body '$.params[0]'}}",
"transactionIndex": "0xb2"
}
],
"logsBloom": "0x00000000400000000000000000000000000100000000000000000000040000000000000000000000000000000000000008000000000000000000000000000000000000000000000008000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010020000000000000000000002000000000000000000000000010000000000000000000000010100000000200000002000000004000000000000000000000000000000000000000002000000000000000000000000000000000100000000000000000000000000000004000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": "0x13b2639533ec7741172563b490b64cde14a34258",
"transactionHash": "0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd",
"transactionIndex": "0xb2",
"type": "0x2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"priority": 1,
"request": {
"url": "/",
"method": "POST",
"bodyPatterns": [
{
"equalToJson": {
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",
"params": [
"0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd"
]
},
"ignoreArrayOrder": true,
"ignoreExtraElements": true
}
]
},
"response": {
"status": 200,
"bodyFileName": "body-eth_getTransactionByHashERC20_proxied.json",
"headers": {
"Content-Type": "application/json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"priority": 1,
"request": {
"url": "/",
"method": "POST",
"bodyPatterns": [
{
"equalToJson": {
"jsonrpc": "2.0",
"method": "eth_getTransactionReceipt",
"params": [
"0x384cf237da4ed3592b5140ab1ff5bbbad8b06abef3a5e2ae250d0f4333ea27dd"
]
},
"ignoreArrayOrder": true,
"ignoreExtraElements": true
}
]
},
"response": {
"status": 200,
"bodyFileName": "body-eth_getTransactionReceiptERC20_proxied.json",
"headers": {
"Content-Type": "application/json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package onlydust.com.marketplace.api.infura;

public class NotAnERC20TransferException extends RuntimeException {
public NotAnERC20TransferException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.ContractGasProvider;
import org.web3j.tx.gas.DefaultGasProvider;
Expand All @@ -21,6 +22,7 @@
import java.math.BigInteger;
import java.util.HexFormat;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import static java.util.concurrent.CompletableFuture.completedFuture;
Expand Down Expand Up @@ -88,6 +90,15 @@ public static ERC20Contract load(String contractAddress, Web3j web3j, Credential
return new ERC20Contract(contractAddress, web3j, credentials, contractGasProvider);
}

public static Optional<ERC20Contract> fromTransferReceipt(TransactionReceipt receipt, Web3j web3j, Credentials credentials,
ContractGasProvider contractGasProvider) {
final var calledContract = ERC20Contract.load(receipt.getTo(), web3j, credentials, contractGasProvider);
// Returns the contract from which the Transfer event originated
return calledContract.getTransferEvents(receipt).stream()
.map(l -> ERC20Contract.load(l.log.getAddress(), web3j, credentials, contractGasProvider))
.findFirst();
}

public CompletableFuture<String> nameWithBinaryFallback() {
return super.name().sendAsync().thenCompose(r -> r.isEmpty() ? binaryCallAsync("name") : completedFuture(r));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import onlydust.com.marketplace.accounting.domain.port.out.BlockchainTransactionStoragePort;
import onlydust.com.marketplace.api.infura.NotAnERC20TransferException;
import onlydust.com.marketplace.api.infura.Web3Client;
import onlydust.com.marketplace.kernel.model.blockchain.Ethereum;
import onlydust.com.marketplace.kernel.model.blockchain.evm.EvmTransaction;
Expand Down Expand Up @@ -61,7 +62,8 @@ private Optional<EvmTransaction> tryFromERC20(final @NonNull EthBlock.Block bloc
final @NonNull Transaction transaction,
final @NonNull TransactionReceipt receipt) {
try {
final var erc20 = ERC20Contract.load(transaction.getTo(), web3j, credentials, gasPriceProvider);
final var erc20 = ERC20Contract.fromTransferReceipt(receipt, web3j, credentials, gasPriceProvider)
.orElseThrow(() -> new NotAnERC20TransferException("Transaction %s is not an ERC20 transfer".formatted(transaction.getHash())));
final var decimals = erc20.decimals().send();

return erc20.getTransferEvents(receipt).stream()
Expand All @@ -73,10 +75,10 @@ private Optional<EvmTransaction> tryFromERC20(final @NonNull EthBlock.Block bloc
Ethereum.accountAddress(l._from),
Ethereum.accountAddress(l._to),
new BigDecimal(l._value, decimals.intValue()),
Ethereum.contractAddress(transaction.getTo())))
Ethereum.contractAddress(erc20.getContractAddress())))
.findFirst()
.map(identity());
} catch (ContractCallException e) {
} catch (ContractCallException | NotAnERC20TransferException e) {
return Optional.empty();
} catch (Exception e) {
throw internalServerError("Unable to fetch ERC20 decimals at address %s".formatted(transaction.getTo()), e);
Expand Down

0 comments on commit bc68049

Please sign in to comment.