parent
325cb1d2bb
commit
65dd29069e
@ -0,0 +1,720 @@ |
|||||||
|
defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do |
||||||
|
use BlockScoutWeb.ConnCase |
||||||
|
|
||||||
|
import Mox |
||||||
|
|
||||||
|
alias BlockScoutWeb.AddressContractView |
||||||
|
alias BlockScoutWeb.Models.UserFromAuth |
||||||
|
alias Explorer.Chain.Address |
||||||
|
|
||||||
|
describe "/smart-contracts/{address_hash}" do |
||||||
|
test "get 404 on non existing SC", %{conn: conn} do |
||||||
|
address = build(:address) |
||||||
|
|
||||||
|
request = get(conn, "/api/v2/smart-contracts/#{address.hash}") |
||||||
|
|
||||||
|
assert %{"message" => "Not found"} = json_response(request, 404) |
||||||
|
end |
||||||
|
|
||||||
|
test "get 422 on invalid address", %{conn: conn} do |
||||||
|
request = get(conn, "/api/v2/smart-contracts/0x") |
||||||
|
|
||||||
|
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) |
||||||
|
end |
||||||
|
|
||||||
|
test "get smart-contract", %{conn: conn} do |
||||||
|
target_contract = insert(:smart_contract) |
||||||
|
|
||||||
|
tx = |
||||||
|
insert(:transaction, |
||||||
|
created_contract_address_hash: target_contract.address_hash, |
||||||
|
input: |
||||||
|
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" |
||||||
|
) |
||||||
|
|> with_block() |
||||||
|
|
||||||
|
correct_response = %{ |
||||||
|
"verified_twin_address_hash" => nil, |
||||||
|
"is_verified" => true, |
||||||
|
"is_changed_bytecode" => false, |
||||||
|
"is_partially_verified" => target_contract.partially_verified, |
||||||
|
"is_fully_verified" => true, |
||||||
|
"is_verified_via_sourcify" => target_contract.verified_via_sourcify, |
||||||
|
"is_vyper_contract" => target_contract.is_vyper_contract, |
||||||
|
"minimal_proxy_address_hash" => nil, |
||||||
|
"sourcify_repo_url" => |
||||||
|
if(target_contract.verified_via_sourcify, |
||||||
|
do: AddressContractView.sourcify_repo_url(target_contract.address_hash, target_contract.partially_verified) |
||||||
|
), |
||||||
|
"can_be_visualized_via_sol2uml" => false, |
||||||
|
"name" => target_contract && target_contract.name, |
||||||
|
"compiler_version" => target_contract.compiler_version, |
||||||
|
"optimization_enabled" => if(target_contract.is_vyper_contract, do: nil, else: target_contract.optimization), |
||||||
|
"optimization_runs" => target_contract.optimization_runs, |
||||||
|
"evm_version" => target_contract.evm_version, |
||||||
|
"verified_at" => target_contract.inserted_at |> to_string() |> String.replace(" ", "T"), |
||||||
|
"source_code" => target_contract.contract_source_code, |
||||||
|
"file_path" => target_contract.file_path, |
||||||
|
"additional_sources" => [], |
||||||
|
"compiler_settings" => target_contract.compiler_settings, |
||||||
|
"external_libraries" => target_contract.external_libraries, |
||||||
|
"constructor_args" => target_contract.constructor_arguments, |
||||||
|
"decoded_constructor_args" => nil, |
||||||
|
"is_self_destructed" => false, |
||||||
|
"deployed_bytecode" => |
||||||
|
"0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", |
||||||
|
"creation_bytecode" => |
||||||
|
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" |
||||||
|
} |
||||||
|
|
||||||
|
blockchain_get_code_mock() |
||||||
|
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") |
||||||
|
response = json_response(request, 200) |
||||||
|
|
||||||
|
assert ^correct_response = Map.drop(response, ["abi"]) |
||||||
|
assert response["abi"] == target_contract.abi |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "/smart-contracts/{address_hash}/methods-read" do |
||||||
|
test "get 404 on non existing SC", %{conn: conn} do |
||||||
|
address = build(:address) |
||||||
|
|
||||||
|
request = get(conn, "/api/v2/smart-contracts/#{address.hash}/methods-read") |
||||||
|
|
||||||
|
assert %{"message" => "Not found"} = json_response(request, 404) |
||||||
|
end |
||||||
|
|
||||||
|
test "get 422 on invalid address", %{conn: conn} do |
||||||
|
request = get(conn, "/api/v2/smart-contracts/0x/methods-read") |
||||||
|
|
||||||
|
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) |
||||||
|
end |
||||||
|
|
||||||
|
test "get read-methods", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
target_contract = insert(:smart_contract, abi: abi) |
||||||
|
|
||||||
|
blockchain_eth_call_mock() |
||||||
|
|
||||||
|
request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read") |
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert %{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [ |
||||||
|
%{ |
||||||
|
"type" => "address", |
||||||
|
"name" => "", |
||||||
|
"internalType" => "address", |
||||||
|
"value" => "0xfffffffffffffffffffffffffffffffffffffffe" |
||||||
|
} |
||||||
|
], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [], |
||||||
|
"method_id" => "ab470f05" |
||||||
|
} in response |
||||||
|
|
||||||
|
assert %{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool", "value" => ""}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}], |
||||||
|
"method_id" => "c683630d" |
||||||
|
} in response |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "/smart-contracts/{address_hash}/query-read-method" do |
||||||
|
test "get 404 on non existing SC", %{conn: conn} do |
||||||
|
address = build(:address) |
||||||
|
|
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/#{address.hash}/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert %{"message" => "Not found"} = json_response(request, 404) |
||||||
|
end |
||||||
|
|
||||||
|
test "get 422 on invalid address", %{conn: conn} do |
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/0x/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) |
||||||
|
end |
||||||
|
|
||||||
|
test "query-read-method", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
method: "eth_call", |
||||||
|
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _] |
||||||
|
} |
||||||
|
], |
||||||
|
_opts -> |
||||||
|
{:ok, |
||||||
|
[ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
jsonrpc: "2.0", |
||||||
|
result: "0x0000000000000000000000000000000000000000000000000000000000000001" |
||||||
|
} |
||||||
|
]} |
||||||
|
end |
||||||
|
) |
||||||
|
|
||||||
|
target_contract = insert(:smart_contract, abi: abi) |
||||||
|
|
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert %{ |
||||||
|
"is_error" => false, |
||||||
|
"result" => %{"names" => ["bool"], "output" => [%{"type" => "bool", "value" => true}]} |
||||||
|
} == response |
||||||
|
end |
||||||
|
|
||||||
|
test "query-read-method returns error 1", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
method: "eth_call", |
||||||
|
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _] |
||||||
|
} |
||||||
|
], |
||||||
|
_opts -> |
||||||
|
{:ok, [%{id: id, jsonrpc: "2.0", error: %{code: "12345", message: "Error message"}}]} |
||||||
|
end |
||||||
|
) |
||||||
|
|
||||||
|
target_contract = insert(:smart_contract, abi: abi) |
||||||
|
|
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert %{"is_error" => true, "result" => %{"code" => "12345", "message" => "Error message"}} == response |
||||||
|
end |
||||||
|
|
||||||
|
test "query-read-method returns error 2", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
method: "eth_call", |
||||||
|
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _] |
||||||
|
} |
||||||
|
], |
||||||
|
_opts -> |
||||||
|
{:error, {:bad_gateway, "request_url"}} |
||||||
|
end |
||||||
|
) |
||||||
|
|
||||||
|
target_contract = insert(:smart_contract, abi: abi) |
||||||
|
|
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
assert %{"is_error" => true, "result" => %{"error" => "Bad gateway"}} == response |
||||||
|
end |
||||||
|
|
||||||
|
test "query-read-method returns error 3", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
method: "eth_call", |
||||||
|
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _] |
||||||
|
} |
||||||
|
], |
||||||
|
_opts -> |
||||||
|
raise FunctionClauseError |
||||||
|
end |
||||||
|
) |
||||||
|
|
||||||
|
target_contract = insert(:smart_contract, abi: abi) |
||||||
|
|
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert %{"is_error" => true, "result" => %{"error" => "no function clause matches"}} == response |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "/smart-contracts/{address_hash}/methods-write" do |
||||||
|
test "get 404 on non existing SC", %{conn: conn} do |
||||||
|
address = build(:address) |
||||||
|
|
||||||
|
request = get(conn, "/api/v2/smart-contracts/#{address.hash}/methods-write") |
||||||
|
|
||||||
|
assert %{"message" => "Not found"} = json_response(request, 404) |
||||||
|
end |
||||||
|
|
||||||
|
test "get 422 on invalid address", %{conn: conn} do |
||||||
|
request = get(conn, "/api/v2/smart-contracts/0x/methods-write") |
||||||
|
|
||||||
|
assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) |
||||||
|
end |
||||||
|
|
||||||
|
test "get write-methods", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
target_contract = insert(:smart_contract, abi: abi) |
||||||
|
|
||||||
|
request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-write") |
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] == response |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "/smart-contracts/{address_hash}/methods-[write/read] & query read method custom abi" do |
||||||
|
setup %{conn: conn} do |
||||||
|
auth = build(:auth) |
||||||
|
|
||||||
|
{:ok, user} = UserFromAuth.find_or_create(auth) |
||||||
|
|
||||||
|
{:ok, conn: Plug.Test.init_test_session(conn, current_user: user)} |
||||||
|
end |
||||||
|
|
||||||
|
test "get write method from custom abi", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
custom_abi = :custom_abi |> build() |> Map.replace("abi", abi) |
||||||
|
|
||||||
|
conn |
||||||
|
|> post( |
||||||
|
"/api/account/v1/user/custom_abis", |
||||||
|
custom_abi |
||||||
|
) |
||||||
|
|
||||||
|
request = |
||||||
|
get(conn, "/api/v2/smart-contracts/#{custom_abi["contract_address_hash"]}/methods-write", %{ |
||||||
|
"is_custom_abi" => true |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] == response |
||||||
|
end |
||||||
|
|
||||||
|
test "get read method from custom abi", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
custom_abi = :custom_abi |> build() |> Map.replace("abi", abi) |
||||||
|
|
||||||
|
conn |
||||||
|
|> post( |
||||||
|
"/api/account/v1/user/custom_abis", |
||||||
|
custom_abi |
||||||
|
) |
||||||
|
|
||||||
|
blockchain_eth_call_mock() |
||||||
|
|
||||||
|
request = |
||||||
|
get(conn, "/api/v2/smart-contracts/#{custom_abi["contract_address_hash"]}/methods-read", %{ |
||||||
|
"is_custom_abi" => true |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert %{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [ |
||||||
|
%{ |
||||||
|
"type" => "address", |
||||||
|
"name" => "", |
||||||
|
"internalType" => "address", |
||||||
|
"value" => "0xfffffffffffffffffffffffffffffffffffffffe" |
||||||
|
} |
||||||
|
], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [], |
||||||
|
"method_id" => "ab470f05" |
||||||
|
} in response |
||||||
|
|
||||||
|
assert %{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool", "value" => ""}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}], |
||||||
|
"method_id" => "c683630d" |
||||||
|
} in response |
||||||
|
end |
||||||
|
|
||||||
|
test "query read method", %{conn: conn} do |
||||||
|
abi = [ |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], |
||||||
|
"name" => "getCaller", |
||||||
|
"inputs" => [] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "view", |
||||||
|
"outputs" => [%{"type" => "bool", "name" => "", "internalType" => "bool"}], |
||||||
|
"name" => "isWhitelist", |
||||||
|
"inputs" => [%{"type" => "address", "name" => "_address", "internalType" => "address"}] |
||||||
|
}, |
||||||
|
%{ |
||||||
|
"type" => "function", |
||||||
|
"stateMutability" => "nonpayable", |
||||||
|
"outputs" => [], |
||||||
|
"name" => "disableWhitelist", |
||||||
|
"inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
custom_abi = :custom_abi |> build() |> Map.replace("abi", abi) |
||||||
|
|
||||||
|
conn |
||||||
|
|> post( |
||||||
|
"/api/account/v1/user/custom_abis", |
||||||
|
custom_abi |
||||||
|
) |
||||||
|
|
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
method: "eth_call", |
||||||
|
params: [%{data: "0xc683630d000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"}, _] |
||||||
|
} |
||||||
|
], |
||||||
|
_opts -> |
||||||
|
{:ok, |
||||||
|
[ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
jsonrpc: "2.0", |
||||||
|
result: "0x0000000000000000000000000000000000000000000000000000000000000001" |
||||||
|
} |
||||||
|
]} |
||||||
|
end |
||||||
|
) |
||||||
|
|
||||||
|
request = |
||||||
|
post(conn, "/api/v2/smart-contracts/#{custom_abi["contract_address_hash"]}/query-read-method", %{ |
||||||
|
"contract_type" => "regular", |
||||||
|
"args" => ["0xfffffffffffffffffffffffffffffffffffffffe"], |
||||||
|
"is_custom_abi" => true, |
||||||
|
"method_id" => "c683630d" |
||||||
|
}) |
||||||
|
|
||||||
|
assert response = json_response(request, 200) |
||||||
|
|
||||||
|
assert %{ |
||||||
|
"is_error" => false, |
||||||
|
"result" => %{"names" => ["bool"], "output" => [%{"type" => "bool", "value" => true}]} |
||||||
|
} == response |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp blockchain_get_code_mock do |
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> |
||||||
|
{:ok, |
||||||
|
[ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
jsonrpc: "2.0", |
||||||
|
result: |
||||||
|
"0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" |
||||||
|
} |
||||||
|
]} |
||||||
|
end |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
defp blockchain_eth_call_mock do |
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [%{id: id, method: "eth_call", params: params}], opts -> |
||||||
|
{:ok, |
||||||
|
[ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
jsonrpc: "2.0", |
||||||
|
result: "0x000000000000000000000000fffffffffffffffffffffffffffffffffffffffe" |
||||||
|
} |
||||||
|
]} |
||||||
|
end |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
defp blockchain_eth_call_mock do |
||||||
|
expect( |
||||||
|
EthereumJSONRPC.Mox, |
||||||
|
:json_rpc, |
||||||
|
fn [%{id: id, method: "eth_call", params: params}], opts -> |
||||||
|
{:ok, |
||||||
|
[ |
||||||
|
%{ |
||||||
|
id: id, |
||||||
|
jsonrpc: "2.0", |
||||||
|
result: "0x000000000000000000000000fffffffffffffffffffffffffffffffffffffffe" |
||||||
|
} |
||||||
|
]} |
||||||
|
end |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
defp debug(value, key) do |
||||||
|
require Logger |
||||||
|
Logger.configure(truncate: :infinity) |
||||||
|
Logger.info(key) |
||||||
|
Logger.info(Kernel.inspect(value, limit: :infinity, printable_limit: :infinity)) |
||||||
|
value |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue