parent
f77ff79a36
commit
fe78cbfe39
@ -1,75 +1,186 @@ |
|||||||
import $ from 'jquery' |
import $ from 'jquery' |
||||||
|
|
||||||
$(function () { |
let $currentModal = null |
||||||
$('.js-become-candidate').on('click', function () { |
let modalLocked = false |
||||||
$('#becomeCandidateModal').modal() |
|
||||||
}) |
|
||||||
|
|
||||||
$('.js-validator-info-modal').on('click', function () { |
const spinner = |
||||||
$('#validatorInfoModal').modal() |
` |
||||||
}) |
<span class="loading-spinner-small mr-2"> |
||||||
|
<span class="loading-spinner-block-1"></span> |
||||||
|
<span class="loading-spinner-block-2"></span> |
||||||
|
</span> |
||||||
|
` |
||||||
|
|
||||||
$('.js-move-stake').on('click', function () { |
$(document.body).on('hide.bs.modal', e => { |
||||||
$('#errorStatusModal').modal() |
if (modalLocked) { |
||||||
}) |
e.preventDefault() |
||||||
|
e.stopPropagation() |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
$('.js-remove-pool').on('click', function () { |
$currentModal = null |
||||||
$('#warningStatusModal').modal() |
}) |
||||||
}) |
|
||||||
|
|
||||||
$('.js-copy-address').on('click', function () { |
export function currentModal () { |
||||||
$('#successStatusModal').modal() |
return $currentModal |
||||||
}) |
} |
||||||
|
|
||||||
$('.js-stake-stake').on('click', function () { |
export function openModal ($modal, unclosable) { |
||||||
const modal = '#stakeModal' |
// Hide all tooltips before showing a modal,
|
||||||
const progress = parseInt($(`${modal} .js-stakes-progress-data-progress`).text()) |
// since they are sticking on top of modal
|
||||||
const total = parseInt($(`${modal} .js-stakes-progress-data-total`).text()) |
$('.tooltip').tooltip('hide') |
||||||
|
|
||||||
$(modal).modal() |
if (unclosable) { |
||||||
|
$('.close-modal, .modal-status-button-wrapper', $modal).addClass('hidden') |
||||||
|
$('.modal-status-text', $modal).addClass('m-b-0') |
||||||
|
} |
||||||
|
|
||||||
setupStakesProgress(progress, total, modal) |
if ($currentModal) { |
||||||
|
modalLocked = false |
||||||
|
|
||||||
|
$currentModal |
||||||
|
.one('hidden.bs.modal', () => { |
||||||
|
$modal.modal('show') |
||||||
|
$currentModal = $modal |
||||||
|
if (unclosable) { |
||||||
|
modalLocked = true |
||||||
|
} |
||||||
}) |
}) |
||||||
|
.modal('hide') |
||||||
|
} else { |
||||||
|
$modal.modal('show') |
||||||
|
$currentModal = $modal |
||||||
|
if (unclosable) { |
||||||
|
modalLocked = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export function lockModal ($modal, $submitButton = null, spinnerText = '') { |
||||||
|
$modal.find('.close-modal').attr('disabled', true) |
||||||
|
|
||||||
|
const $button = $submitButton || $modal.find('.btn-add-full') |
||||||
|
|
||||||
|
$button |
||||||
|
.attr('data-text', $button.text()) |
||||||
|
.attr('disabled', true) |
||||||
|
|
||||||
|
const $span = $('span', $button) |
||||||
|
const waitHtml = spinner + (spinnerText ? ` ${spinnerText}` : '') |
||||||
|
|
||||||
|
if ($span.length) { |
||||||
|
$('svg', $button).hide() |
||||||
|
$span.html(waitHtml) |
||||||
|
} else { |
||||||
|
$button.html(waitHtml) |
||||||
|
} |
||||||
|
|
||||||
|
modalLocked = true |
||||||
|
} |
||||||
|
|
||||||
|
export function unlockModal ($modal, $submitButton = null) { |
||||||
|
$modal.find('.close-modal').attr('disabled', false) |
||||||
|
|
||||||
$('.js-withdraw-stake').on('click', function () { |
const $button = $submitButton || $modal.find('.btn-add-full') |
||||||
const modal = '#withdrawModal' |
const buttonText = $button.attr('data-text') |
||||||
const progress = parseInt($(`${modal} .js-stakes-progress-data-progress`).text()) |
|
||||||
const total = parseInt($(`${modal} .js-stakes-progress-data-total`).text()) |
|
||||||
|
|
||||||
$(modal).modal() |
$button.attr('disabled', false) |
||||||
|
|
||||||
setupStakesProgress(progress, total, modal) |
const $span = $('span', $button) |
||||||
|
if ($span.length) { |
||||||
|
$('svg', $button).show() |
||||||
|
$span.text(buttonText) |
||||||
|
} else { |
||||||
|
$button.text(buttonText) |
||||||
|
} |
||||||
|
|
||||||
|
modalLocked = false |
||||||
|
} |
||||||
|
|
||||||
|
export function openErrorModal (title, text, unclosable) { |
||||||
|
const $modal = $('#errorStatusModal') |
||||||
|
$modal.find('.modal-status-title').text(title) |
||||||
|
$modal.find('.modal-status-text').html(text) |
||||||
|
openModal($modal, unclosable) |
||||||
|
} |
||||||
|
|
||||||
|
export function openWarningModal (title, text) { |
||||||
|
const $modal = $('#warningStatusModal') |
||||||
|
$modal.find('.modal-status-title').text(title) |
||||||
|
$modal.find('.modal-status-text').html(text) |
||||||
|
openModal($modal) |
||||||
|
} |
||||||
|
|
||||||
|
export function openSuccessModal (title, text) { |
||||||
|
const $modal = $('#successStatusModal') |
||||||
|
$modal.find('.modal-status-title').text(title) |
||||||
|
$modal.find('.modal-status-text').html(text) |
||||||
|
openModal($modal) |
||||||
|
} |
||||||
|
|
||||||
|
export function openQuestionModal (title, text, acceptCallback = null, exceptCallback = null, acceptText = 'Yes', exceptText = 'No') { |
||||||
|
const $modal = $('#questionStatusModal') |
||||||
|
const $closeButton = $modal.find('.close-modal') |
||||||
|
|
||||||
|
$closeButton.attr('disabled', false) |
||||||
|
|
||||||
|
$modal.find('.modal-status-title').text(title) |
||||||
|
$modal.find('.modal-status-text').text(text) |
||||||
|
|
||||||
|
const $accept = $modal.find('.btn-line.accept') |
||||||
|
const $except = $modal.find('.btn-line.except') |
||||||
|
|
||||||
|
$accept |
||||||
|
.removeAttr('data-dismiss') |
||||||
|
.removeAttr('disabled') |
||||||
|
.unbind('click') |
||||||
|
.find('.btn-line-text').text(acceptText) |
||||||
|
|
||||||
|
$except |
||||||
|
.removeAttr('data-dismiss') |
||||||
|
.removeAttr('disabled') |
||||||
|
.unbind('click') |
||||||
|
.find('.btn-line-text').text(exceptText) |
||||||
|
|
||||||
|
if (acceptCallback) { |
||||||
|
$accept.on('click', event => { |
||||||
|
$closeButton.attr('disabled', true) |
||||||
|
|
||||||
|
$accept |
||||||
|
.unbind('click') |
||||||
|
.attr('disabled', true) |
||||||
|
.find('.btn-line-text').html(spinner) |
||||||
|
$except |
||||||
|
.unbind('click') |
||||||
|
.removeAttr('data-dismiss') |
||||||
|
.attr('disabled', true) |
||||||
|
|
||||||
|
modalLocked = true |
||||||
|
acceptCallback($modal, event) |
||||||
}) |
}) |
||||||
|
} else { |
||||||
|
$accept.attr('data-dismiss', 'modal') |
||||||
|
} |
||||||
|
|
||||||
function setupStakesProgress (progress, total, modal) { |
if (exceptCallback) { |
||||||
// const stakeProgress = $(`${modal} .js-stakes-progress`)
|
$except.on('click', event => { |
||||||
// const primaryColor = $('.btn-full-primary').css('background-color')
|
$closeButton.attr('disabled', true) |
||||||
// const backgroundColors = [
|
|
||||||
// primaryColor,
|
$except |
||||||
// 'rgba(202, 199, 226, 0.5)'
|
.unbind('click') |
||||||
// ]
|
.attr('disabled', true) |
||||||
// const progressBackground = total - progress
|
.find('.btn-line-text').html(spinner) |
||||||
|
$accept |
||||||
// // eslint-disable-next-line no-unused-vars
|
.unbind('click') |
||||||
// const myChart = new window.Chart(stakeProgress, {
|
.attr('disabled', true) |
||||||
// type: 'doughnut',
|
.removeAttr('data-dismiss') |
||||||
// data: {
|
|
||||||
// datasets: [{
|
modalLocked = true |
||||||
// data: [progress, progressBackground],
|
exceptCallback($modal, event) |
||||||
// backgroundColor: backgroundColors,
|
}) |
||||||
// hoverBackgroundColor: backgroundColors,
|
} else { |
||||||
// borderWidth: 0
|
$except.attr('data-dismiss', 'modal') |
||||||
// }]
|
|
||||||
// },
|
|
||||||
// options: {
|
|
||||||
// cutoutPercentage: 80,
|
|
||||||
// legend: {
|
|
||||||
// display: false
|
|
||||||
// },
|
|
||||||
// tooltips: {
|
|
||||||
// enabled: false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
} |
} |
||||||
}) |
|
||||||
|
openModal($modal) |
||||||
|
} |
||||||
|
@ -0,0 +1,99 @@ |
|||||||
|
import $ from 'jquery' |
||||||
|
import { walletEnabled, getCurrentAccount } from './write.js' |
||||||
|
import { openErrorModal, openWarningModal, openSuccessModal } from '../modals.js' |
||||||
|
|
||||||
|
const loadFunctions = (element) => { |
||||||
|
const $element = $(element) |
||||||
|
const url = $element.data('url') |
||||||
|
const hash = $element.data('hash') |
||||||
|
const type = $element.data('type') |
||||||
|
const action = $element.data('action') |
||||||
|
|
||||||
|
$.get( |
||||||
|
url, |
||||||
|
{ hash: hash, type: type, action: action }, |
||||||
|
response => $element.html(response) |
||||||
|
) |
||||||
|
.done(function () { |
||||||
|
$('[data-function]').each((_, element) => { |
||||||
|
readWriteFunction(element) |
||||||
|
}) |
||||||
|
}) |
||||||
|
.fail(function (response) { |
||||||
|
$element.html(response.statusText) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
const readWriteFunction = (element) => { |
||||||
|
const $element = $(element) |
||||||
|
const $form = $element.find('[data-function-form]') |
||||||
|
|
||||||
|
const $responseContainer = $element.find('[data-function-response]') |
||||||
|
|
||||||
|
$form.on('submit', (event) => { |
||||||
|
const action = $form.data('action') |
||||||
|
event.preventDefault() |
||||||
|
|
||||||
|
if (action === 'read') { |
||||||
|
const url = $form.data('url') |
||||||
|
const $functionName = $form.find('input[name=function_name]') |
||||||
|
const $functionInputs = $form.find('input[name=function_input]') |
||||||
|
|
||||||
|
const args = $.map($functionInputs, element => { |
||||||
|
return $(element).val() |
||||||
|
}) |
||||||
|
|
||||||
|
const data = { |
||||||
|
function_name: $functionName.val(), |
||||||
|
args |
||||||
|
} |
||||||
|
|
||||||
|
$.get(url, data, response => $responseContainer.html(response)) |
||||||
|
} else { |
||||||
|
walletEnabled() |
||||||
|
.then((isWalletEnabled) => { |
||||||
|
if (isWalletEnabled) { |
||||||
|
const functionName = $form.find('input[name=function_name]').val() |
||||||
|
|
||||||
|
const $functionInputs = $form.find('input[name=function_input]') |
||||||
|
const args = $.map($functionInputs, element => { |
||||||
|
return $(element).val() |
||||||
|
}) |
||||||
|
|
||||||
|
const contractAddress = $form.data('contract-address') |
||||||
|
const contractAbi = $form.data('contract-abi') |
||||||
|
|
||||||
|
getCurrentAccount() |
||||||
|
.then(currentAccount => { |
||||||
|
const TargetContract = new window.web3.eth.Contract(contractAbi, contractAddress) |
||||||
|
|
||||||
|
TargetContract.methods[functionName](...args).send({ from: currentAccount }) |
||||||
|
.on('error', function (error) { |
||||||
|
openErrorModal(`Error in sending transaction for method "${functionName}"`, error, false) |
||||||
|
}) |
||||||
|
.on('transactionHash', function (txHash) { |
||||||
|
const getTxReceipt = (txHash) => { |
||||||
|
window.web3.eth.getTransactionReceipt(txHash) |
||||||
|
.then(txReceipt => { |
||||||
|
if (txReceipt) { |
||||||
|
openSuccessModal('Success', `Successfully sent <a href="/tx/${txHash}">transaction</a> for method "${functionName}"`) |
||||||
|
clearInterval(txReceiptPollingIntervalId) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
const txReceiptPollingIntervalId = setInterval(() => { getTxReceipt(txHash) }, 5 * 1000) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
openWarningModal('Unauthorized', 'You haven\'t approved the reading of account list from your MetaMask/Nifty wallet or MetaMask/Nifty wallet is not installed.') |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
const container = $('[data-smart-contract-functions]') |
||||||
|
|
||||||
|
if (container.length) { |
||||||
|
loadFunctions(container) |
||||||
|
} |
@ -1,3 +1,3 @@ |
|||||||
import './read_only_functions' |
import './functions' |
||||||
import './wei_ether_converter' |
import './wei_ether_converter' |
||||||
import '../../app' |
import '../../app' |
||||||
|
@ -1,54 +0,0 @@ |
|||||||
import $ from 'jquery' |
|
||||||
|
|
||||||
const loadFunctions = (element) => { |
|
||||||
const $element = $(element) |
|
||||||
const url = $element.data('url') |
|
||||||
const hash = $element.data('hash') |
|
||||||
const type = $element.data('type') |
|
||||||
|
|
||||||
$.get( |
|
||||||
url, |
|
||||||
{ hash: hash, type: type }, |
|
||||||
response => $element.html(response) |
|
||||||
) |
|
||||||
.done(function () { |
|
||||||
$('[data-function]').each((_, element) => { |
|
||||||
readFunction(element) |
|
||||||
}) |
|
||||||
}) |
|
||||||
.fail(function (response) { |
|
||||||
$element.html(response.statusText) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
const readFunction = (element) => { |
|
||||||
const $element = $(element) |
|
||||||
const $form = $element.find('[data-function-form]') |
|
||||||
|
|
||||||
const $responseContainer = $element.find('[data-function-response]') |
|
||||||
|
|
||||||
$form.on('submit', (event) => { |
|
||||||
event.preventDefault() |
|
||||||
|
|
||||||
const url = $form.data('url') |
|
||||||
const $functionName = $form.find('input[name=function_name]') |
|
||||||
const $functionInputs = $form.find('input[name=function_input]') |
|
||||||
|
|
||||||
const args = $.map($functionInputs, element => { |
|
||||||
return $(element).val() |
|
||||||
}) |
|
||||||
|
|
||||||
const data = { |
|
||||||
function_name: $functionName.val(), |
|
||||||
args |
|
||||||
} |
|
||||||
|
|
||||||
$.get(url, data, response => $responseContainer.html(response)) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
const container = $('[data-smart-contract-functions]') |
|
||||||
|
|
||||||
if (container.length) { |
|
||||||
loadFunctions(container) |
|
||||||
} |
|
@ -0,0 +1,29 @@ |
|||||||
|
import Web3 from 'web3' |
||||||
|
|
||||||
|
export const walletEnabled = () => { |
||||||
|
if (window.ethereum) { |
||||||
|
window.web3 = new Web3(window.ethereum) |
||||||
|
if (window.ethereum._state && window.ethereum._state.isUnlocked) { // Nifty Wallet
|
||||||
|
window.web3 = new Web3(window.web3.currentProvider) |
||||||
|
return Promise.resolve(true) |
||||||
|
} else if (window.ethereum._state && window.ethereum._state.isUnlocked === false) { // Nifty Wallet
|
||||||
|
return Promise.resolve(false) |
||||||
|
} else { |
||||||
|
window.ethereum.enable() |
||||||
|
window.web3 = new Web3(window.web3.currentProvider) |
||||||
|
return Promise.resolve(true) |
||||||
|
} |
||||||
|
} else if (window.web3) { |
||||||
|
window.web3 = new Web3(window.web3.currentProvider) |
||||||
|
return Promise.resolve(true) |
||||||
|
} else { |
||||||
|
return Promise.resolve(false) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const getCurrentAccount = async () => { |
||||||
|
const accounts = await window.web3.eth.getAccounts() |
||||||
|
const account = accounts[0] ? accounts[0].toLowerCase() : null |
||||||
|
|
||||||
|
return account |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
import '../lib/smart_contract/write' |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@ |
|||||||
|
# credo:disable-for-this-file |
||||||
|
# |
||||||
|
# When moving the calls to ajax, this controller became very similar to the |
||||||
|
# `address_contract_controller`, but both are necessary until we are able to |
||||||
|
# address a better way to organize the controllers. |
||||||
|
# |
||||||
|
# So, for now, I'm adding this comment to disable the credo check for this file. |
||||||
|
defmodule BlockScoutWeb.AddressWriteContractController do |
||||||
|
use BlockScoutWeb, :controller |
||||||
|
|
||||||
|
alias Explorer.{Chain, Market} |
||||||
|
alias Explorer.Chain.Address |
||||||
|
alias Explorer.ExchangeRates.Token |
||||||
|
alias Indexer.Fetcher.CoinBalanceOnDemand |
||||||
|
|
||||||
|
def index(conn, %{"address_id" => address_hash_string}) do |
||||||
|
address_options = [ |
||||||
|
necessity_by_association: %{ |
||||||
|
:contracts_creation_internal_transaction => :optional, |
||||||
|
:names => :optional, |
||||||
|
:smart_contract => :optional, |
||||||
|
:token => :optional, |
||||||
|
:contracts_creation_transaction => :optional |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), |
||||||
|
{:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), |
||||||
|
false <- is_nil(address.smart_contract) do |
||||||
|
render( |
||||||
|
conn, |
||||||
|
"index.html", |
||||||
|
address: address, |
||||||
|
type: :regular, |
||||||
|
action: :write, |
||||||
|
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), |
||||||
|
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), |
||||||
|
counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}) |
||||||
|
) |
||||||
|
else |
||||||
|
_ -> |
||||||
|
not_found(conn) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1 @@ |
|||||||
|
<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> |
@ -0,0 +1,20 @@ |
|||||||
|
<section class="container"> |
||||||
|
|
||||||
|
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %> |
||||||
|
|
||||||
|
<div class="card"> |
||||||
|
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> |
||||||
|
<!-- loaded via AJAX --> |
||||||
|
<div class="card-body" data-smart-contract-functions data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>"> |
||||||
|
<div> |
||||||
|
<span class="loading-spinner-small mr-2"> |
||||||
|
<span class="loading-spinner-block-1"></span> |
||||||
|
<span class="loading-spinner-block-2"></span> |
||||||
|
</span> |
||||||
|
<%= gettext("Loading...") %> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/smart-contract-helpers.js") %>"></script> |
||||||
|
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/write_contract.js") %>"></script> |
||||||
|
</section> |
@ -1,7 +1,13 @@ |
|||||||
defmodule BlockScoutWeb.AddressReadContractView do |
defmodule BlockScoutWeb.AddressReadContractView do |
||||||
use BlockScoutWeb, :view |
use BlockScoutWeb, :view |
||||||
|
|
||||||
def queryable?(inputs), do: Enum.any?(inputs) |
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs) |
||||||
|
|
||||||
|
def queryable?(inputs) when is_nil(inputs), do: false |
||||||
|
|
||||||
|
def outputs?(outputs) when not is_nil(outputs), do: Enum.any?(outputs) |
||||||
|
|
||||||
|
def outputs?(outputs) when is_nil(outputs), do: false |
||||||
|
|
||||||
def address?(type), do: type == "address" |
def address?(type), do: type == "address" |
||||||
end |
end |
||||||
|
@ -1,7 +1,13 @@ |
|||||||
defmodule BlockScoutWeb.AddressReadProxyView do |
defmodule BlockScoutWeb.AddressReadProxyView do |
||||||
use BlockScoutWeb, :view |
use BlockScoutWeb, :view |
||||||
|
|
||||||
def queryable?(inputs), do: Enum.any?(inputs) |
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs) |
||||||
|
|
||||||
|
def queryable?(inputs) when is_nil(inputs), do: false |
||||||
|
|
||||||
|
def outputs?(outputs) when not is_nil(outputs), do: Enum.any?(outputs) |
||||||
|
|
||||||
|
def outputs?(outputs) when is_nil(outputs), do: false |
||||||
|
|
||||||
def address?(type), do: type == "address" |
def address?(type), do: type == "address" |
||||||
end |
end |
||||||
|
@ -0,0 +1,13 @@ |
|||||||
|
defmodule BlockScoutWeb.AddressWriteContractView do |
||||||
|
use BlockScoutWeb, :view |
||||||
|
|
||||||
|
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs) |
||||||
|
|
||||||
|
def queryable?(inputs) when is_nil(inputs), do: false |
||||||
|
|
||||||
|
def outputs?(outputs) when not is_nil(outputs), do: Enum.any?(outputs) |
||||||
|
|
||||||
|
def outputs?(outputs) when is_nil(outputs), do: false |
||||||
|
|
||||||
|
def address?(type), do: type == "address" |
||||||
|
end |
@ -0,0 +1,28 @@ |
|||||||
|
defmodule Explorer.SmartContract.Writer do |
||||||
|
@moduledoc """ |
||||||
|
Generates smart-contract transactions |
||||||
|
""" |
||||||
|
|
||||||
|
alias Explorer.Chain |
||||||
|
|
||||||
|
@spec write_functions(Hash.t()) :: [%{}] |
||||||
|
def write_functions(contract_address_hash) do |
||||||
|
abi = |
||||||
|
contract_address_hash |
||||||
|
|> Chain.address_hash_to_smart_contract() |
||||||
|
|> Map.get(:abi) |
||||||
|
|
||||||
|
case abi do |
||||||
|
nil -> |
||||||
|
[] |
||||||
|
|
||||||
|
_ -> |
||||||
|
abi |
||||||
|
|> Enum.filter( |
||||||
|
&(&1["type"] !== "event" && |
||||||
|
(&1["stateMutability"] == "nonpayable" || &1["stateMutability"] == "payable" || &1["payable"] || |
||||||
|
(!&1["payable"] && !&1["constant"] && !&1["stateMutability"]))) |
||||||
|
) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue