parent
cd60db1830
commit
3877fb170d
@ -0,0 +1,81 @@ |
||||
defmodule BlockScoutWeb.AddressWriteContractControllerTest do |
||||
use BlockScoutWeb.ConnCase, async: true |
||||
|
||||
alias Explorer.ExchangeRates.Token |
||||
alias Explorer.Chain.Address |
||||
|
||||
import Mox |
||||
|
||||
describe "GET index/3" do |
||||
setup :set_mox_global |
||||
|
||||
setup do |
||||
configuration = Application.get_env(:explorer, :checksum_function) |
||||
Application.put_env(:explorer, :checksum_function, :eth) |
||||
|
||||
:ok |
||||
|
||||
on_exit(fn -> |
||||
Application.put_env(:explorer, :checksum_function, configuration) |
||||
end) |
||||
end |
||||
|
||||
test "with invalid address hash", %{conn: conn} do |
||||
conn = get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) |
||||
|
||||
assert html_response(conn, 404) |
||||
end |
||||
|
||||
test "with valid address that is not a contract", %{conn: conn} do |
||||
address = insert(:address) |
||||
|
||||
conn = get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) |
||||
|
||||
assert html_response(conn, 404) |
||||
end |
||||
|
||||
test "successfully renders the page when the address is a contract", %{conn: conn} do |
||||
contract_address = insert(:contract_address) |
||||
|
||||
transaction = insert(:transaction, from_address: contract_address) |> with_block() |
||||
|
||||
insert( |
||||
:internal_transaction_create, |
||||
index: 0, |
||||
transaction: transaction, |
||||
created_contract_address: contract_address, |
||||
block_hash: transaction.block_hash, |
||||
block_index: 0 |
||||
) |
||||
|
||||
insert(:smart_contract, address_hash: contract_address.hash) |
||||
|
||||
conn = |
||||
get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) |
||||
|
||||
assert html_response(conn, 200) |
||||
assert contract_address.hash == conn.assigns.address.hash |
||||
assert %Token{} = conn.assigns.exchange_rate |
||||
end |
||||
|
||||
test "returns not found for an unverified contract", %{conn: conn} do |
||||
contract_address = insert(:contract_address) |
||||
|
||||
transaction = insert(:transaction, from_address: contract_address) |> with_block() |
||||
|
||||
insert( |
||||
:internal_transaction_create, |
||||
index: 0, |
||||
transaction: transaction, |
||||
created_contract_address: contract_address, |
||||
block_hash: transaction.block_hash, |
||||
block_index: 0 |
||||
) |
||||
|
||||
conn = |
||||
get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) |
||||
|
||||
assert html_response(conn, 404) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,81 @@ |
||||
defmodule BlockScoutWeb.AddressWriteProxyControllerTest do |
||||
use BlockScoutWeb.ConnCase, async: true |
||||
|
||||
alias Explorer.ExchangeRates.Token |
||||
alias Explorer.Chain.Address |
||||
|
||||
import Mox |
||||
|
||||
describe "GET index/3" do |
||||
setup :set_mox_global |
||||
|
||||
setup do |
||||
configuration = Application.get_env(:explorer, :checksum_function) |
||||
Application.put_env(:explorer, :checksum_function, :eth) |
||||
|
||||
:ok |
||||
|
||||
on_exit(fn -> |
||||
Application.put_env(:explorer, :checksum_function, configuration) |
||||
end) |
||||
end |
||||
|
||||
test "with invalid address hash", %{conn: conn} do |
||||
conn = get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) |
||||
|
||||
assert html_response(conn, 404) |
||||
end |
||||
|
||||
test "with valid address that is not a contract", %{conn: conn} do |
||||
address = insert(:address) |
||||
|
||||
conn = get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) |
||||
|
||||
assert html_response(conn, 404) |
||||
end |
||||
|
||||
test "successfully renders the page when the address is a contract", %{conn: conn} do |
||||
contract_address = insert(:contract_address) |
||||
|
||||
transaction = insert(:transaction, from_address: contract_address) |> with_block() |
||||
|
||||
insert( |
||||
:internal_transaction_create, |
||||
index: 0, |
||||
transaction: transaction, |
||||
created_contract_address: contract_address, |
||||
block_hash: transaction.block_hash, |
||||
block_index: 0 |
||||
) |
||||
|
||||
insert(:smart_contract, address_hash: contract_address.hash) |
||||
|
||||
conn = |
||||
get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) |
||||
|
||||
assert html_response(conn, 200) |
||||
assert contract_address.hash == conn.assigns.address.hash |
||||
assert %Token{} = conn.assigns.exchange_rate |
||||
end |
||||
|
||||
test "returns not found for an unverified contract", %{conn: conn} do |
||||
contract_address = insert(:contract_address) |
||||
|
||||
transaction = insert(:transaction, from_address: contract_address) |> with_block() |
||||
|
||||
insert( |
||||
:internal_transaction_create, |
||||
index: 0, |
||||
transaction: transaction, |
||||
created_contract_address: contract_address, |
||||
block_hash: transaction.block_hash, |
||||
block_index: 0 |
||||
) |
||||
|
||||
conn = |
||||
get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) |
||||
|
||||
assert html_response(conn, 404) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,19 @@ |
||||
defmodule BlockScoutWeb.AddressWriteContractViewTest do |
||||
use BlockScoutWeb.ConnCase, async: true |
||||
|
||||
alias BlockScoutWeb.AddressWriteContractView |
||||
|
||||
describe "queryable?/1" do |
||||
test "returns true if list of inputs is not empty" do |
||||
assert AddressWriteContractView.queryable?([%{"name" => "argument_name", "type" => "uint256"}]) == true |
||||
assert AddressWriteContractView.queryable?([]) == false |
||||
end |
||||
end |
||||
|
||||
describe "address?/1" do |
||||
test "returns true if type equals `address`" do |
||||
assert AddressWriteContractView.address?("address") == true |
||||
assert AddressWriteContractView.address?("uint256") == false |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,19 @@ |
||||
defmodule BlockScoutWeb.AddressWriteProxyViewTest do |
||||
use BlockScoutWeb.ConnCase, async: true |
||||
|
||||
alias BlockScoutWeb.AddressWriteProxyView |
||||
|
||||
describe "queryable?/1" do |
||||
test "returns true if list of inputs is not empty" do |
||||
assert AddressWriteProxyView.queryable?([%{"name" => "argument_name", "type" => "uint256"}]) == true |
||||
assert AddressWriteProxyView.queryable?([]) == false |
||||
end |
||||
end |
||||
|
||||
describe "address?/1" do |
||||
test "returns true if type equals `address`" do |
||||
assert AddressWriteProxyView.address?("address") == true |
||||
assert AddressWriteProxyView.address?("uint256") == false |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,344 @@ |
||||
defmodule Explorer.SmartContract.WriterTest do |
||||
use EthereumJSONRPC.Case |
||||
use Explorer.DataCase |
||||
|
||||
import Mox |
||||
|
||||
alias Explorer.SmartContract.Writer |
||||
|
||||
@abi [ |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [], |
||||
"name" => "upgradeTo", |
||||
"inputs" => [%{"type" => "uint256", "name" => "version"}, %{"type" => "address", "name" => "implementation"}], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "version", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "address", "name" => ""}], |
||||
"name" => "implementation", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "address", "name" => ""}], |
||||
"name" => "upgradeabilityOwner", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "payable", |
||||
"payable" => true, |
||||
"outputs" => [], |
||||
"name" => "upgradeToAndCall", |
||||
"inputs" => [ |
||||
%{"type" => "uint256", "name" => "version"}, |
||||
%{"type" => "address", "name" => "implementation"}, |
||||
%{"type" => "bytes", "name" => "data"} |
||||
], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [], |
||||
"name" => "transferProxyOwnership", |
||||
"inputs" => [%{"type" => "address", "name" => "newOwner"}], |
||||
"constant" => false |
||||
}, |
||||
%{"type" => "fallback", "stateMutability" => "payable", "payable" => true}, |
||||
%{ |
||||
"type" => "event", |
||||
"name" => "ProxyOwnershipTransferred", |
||||
"inputs" => [ |
||||
%{"type" => "address", "name" => "previousOwner", "indexed" => false}, |
||||
%{"type" => "address", "name" => "newOwner", "indexed" => false} |
||||
], |
||||
"anonymous" => false |
||||
}, |
||||
%{ |
||||
"type" => "event", |
||||
"name" => "Upgraded", |
||||
"inputs" => [ |
||||
%{"type" => "uint256", "name" => "version", "indexed" => false}, |
||||
%{"type" => "address", "name" => "implementation", "indexed" => true} |
||||
], |
||||
"anonymous" => false |
||||
} |
||||
] |
||||
|
||||
@implementation_abi [ |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "extraReceiverAmount", |
||||
"inputs" => [%{"type" => "address", "name" => "_receiver"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "bridgesAllowedLength", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "pure", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "bytes4", "name" => ""}], |
||||
"name" => "blockRewardContractId", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "mintedForAccountInBlock", |
||||
"inputs" => [%{"type" => "address", "name" => "_account"}, %{"type" => "uint256", "name" => "_blockNumber"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "mintedForAccount", |
||||
"inputs" => [%{"type" => "address", "name" => "_account"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "mintedInBlock", |
||||
"inputs" => [%{"type" => "uint256", "name" => "_blockNumber"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "mintedTotally", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "pure", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "address[1]", "name" => ""}], |
||||
"name" => "bridgesAllowed", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [], |
||||
"name" => "addExtraReceiver", |
||||
"inputs" => [%{"type" => "uint256", "name" => "_amount"}, %{"type" => "address", "name" => "_receiver"}], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "mintedTotallyByBridge", |
||||
"inputs" => [%{"type" => "address", "name" => "_bridge"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "address", "name" => ""}], |
||||
"name" => "extraReceiverByIndex", |
||||
"inputs" => [%{"type" => "uint256", "name" => "_index"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "bridgeAmount", |
||||
"inputs" => [%{"type" => "address", "name" => "_bridge"}], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "view", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "uint256", "name" => ""}], |
||||
"name" => "extraReceiversLength", |
||||
"inputs" => [], |
||||
"constant" => true |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "address[]", "name" => ""}, %{"type" => "uint256[]", "name" => ""}], |
||||
"name" => "reward", |
||||
"inputs" => [%{"type" => "address[]", "name" => "benefactors"}, %{"type" => "uint16[]", "name" => "kind"}], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "event", |
||||
"name" => "AddedReceiver", |
||||
"inputs" => [ |
||||
%{"type" => "uint256", "name" => "amount", "indexed" => false}, |
||||
%{"type" => "address", "name" => "receiver", "indexed" => true}, |
||||
%{"type" => "address", "name" => "bridge", "indexed" => true} |
||||
], |
||||
"anonymous" => false |
||||
} |
||||
] |
||||
|
||||
doctest Explorer.SmartContract.Writer |
||||
|
||||
setup :verify_on_exit! |
||||
|
||||
describe "write_functions/1" do |
||||
test "fetches the smart contract write functions" do |
||||
smart_contract = |
||||
insert( |
||||
:smart_contract, |
||||
abi: @abi |
||||
) |
||||
|
||||
response = Writer.write_functions(smart_contract.address_hash) |
||||
|
||||
assert [ |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [], |
||||
"name" => "upgradeTo", |
||||
"inputs" => [ |
||||
%{"type" => "uint256", "name" => "version"}, |
||||
%{"type" => "address", "name" => "implementation"} |
||||
], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "payable", |
||||
"payable" => true, |
||||
"outputs" => [], |
||||
"name" => "upgradeToAndCall", |
||||
"inputs" => [ |
||||
%{"type" => "uint256", "name" => "version"}, |
||||
%{"type" => "address", "name" => "implementation"}, |
||||
%{"type" => "bytes", "name" => "data"} |
||||
], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [], |
||||
"name" => "transferProxyOwnership", |
||||
"inputs" => [%{"type" => "address", "name" => "newOwner"}], |
||||
"constant" => false |
||||
}, |
||||
%{"type" => "fallback", "stateMutability" => "payable", "payable" => true} |
||||
] = response |
||||
end |
||||
end |
||||
|
||||
describe "write_functions_proxy/1" do |
||||
test "fetches the smart contract proxy write functions" do |
||||
proxy_smart_contract = |
||||
insert(:smart_contract, |
||||
abi: @abi |
||||
) |
||||
|
||||
implementation_contract_address = insert(:contract_address) |
||||
|
||||
insert(:smart_contract, |
||||
address_hash: implementation_contract_address.hash, |
||||
abi: @implementation_abi |
||||
) |
||||
|
||||
implementation_contract_address_hash_string = |
||||
Base.encode16(implementation_contract_address.hash.bytes, case: :lower) |
||||
|
||||
expect( |
||||
EthereumJSONRPC.Mox, |
||||
:json_rpc, |
||||
fn [%{id: id, method: _, params: [%{data: _, to: _}, _]}], _options -> |
||||
{:ok, |
||||
[ |
||||
%{ |
||||
id: id, |
||||
jsonrpc: "2.0", |
||||
result: "0x000000000000000000000000" <> implementation_contract_address_hash_string |
||||
} |
||||
]} |
||||
end |
||||
) |
||||
|
||||
response = Writer.write_functions_proxy(proxy_smart_contract.address_hash) |
||||
|
||||
assert [ |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [], |
||||
"name" => "addExtraReceiver", |
||||
"inputs" => [ |
||||
%{"type" => "uint256", "name" => "_amount"}, |
||||
%{"type" => "address", "name" => "_receiver"} |
||||
], |
||||
"constant" => false |
||||
}, |
||||
%{ |
||||
"type" => "function", |
||||
"stateMutability" => "nonpayable", |
||||
"payable" => false, |
||||
"outputs" => [%{"type" => "address[]", "name" => ""}, %{"type" => "uint256[]", "name" => ""}], |
||||
"name" => "reward", |
||||
"inputs" => [ |
||||
%{"type" => "address[]", "name" => "benefactors"}, |
||||
%{"type" => "uint16[]", "name" => "kind"} |
||||
], |
||||
"constant" => false |
||||
} |
||||
] = response |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue